From 2cb9f7b111701bfc633bc02f2adee9b4cb364f8a Mon Sep 17 00:00:00 2001 From: Arthur Amstutz Date: Wed, 11 Feb 2026 21:13:16 +0100 Subject: [PATCH 1/3] feat: Add complete cloud loadbalancer sub-resource commands Add CRUD operations for all loadbalancing sub-resources: - Loadbalancer create, delete, stats, associate/create floating IP - Listener list, get, create, edit, delete - Pool list, get, create, edit, delete - Pool Member list, get, create, edit, delete - Health Monitor list, get, create, edit, delete - L7 Policy list, get, create, edit, delete - L7 Rule list, get, create, edit, delete - Log Subscription list, get, create, delete - Log Kind list, get and Log URL generation - Reference get-flavor command Signed-off-by: Arthur Amstutz --- internal/cmd/cloud_network_loadbalancer.go | 615 ++++++++++++++++++ internal/cmd/cloud_reference.go | 7 + internal/services/cloud/cloud_loadbalancer.go | 215 +++++- .../cloud_loadbalancer_health_monitor.go | 144 ++++ .../cloud/cloud_loadbalancer_l7policy.go | 304 +++++++++ .../cloud/cloud_loadbalancer_listener.go | 141 ++++ .../services/cloud/cloud_loadbalancer_log.go | 198 ++++++ .../services/cloud/cloud_loadbalancer_pool.go | 282 ++++++++ internal/services/cloud/cloud_reference.go | 19 + .../loadbalancer-associate-floating-ip.json | 4 + .../loadbalancer-create-floating-ip.json | 3 + .../loadbalancer-create.json | 12 + .../loadbalancer-health-monitor-create.json | 9 + .../loadbalancer-l7policy-create.json | 7 + .../loadbalancer-l7rule-create.json | 5 + .../loadbalancer-listener-create.json | 6 + .../loadbalancer-log-subscription-create.json | 4 + .../loadbalancer-pool-create.json | 6 + .../loadbalancer-pool-member-create.json | 10 + .../cloud_loadbalancer_health_monitor.tmpl | 17 + .../cloud_loadbalancer_l7policy.tmpl | 18 + .../templates/cloud_loadbalancer_l7rule.tmpl | 14 + .../cloud_loadbalancer_listener.tmpl | 15 + .../cloud_loadbalancer_log_subscription.tmpl | 13 + .../templates/cloud_loadbalancer_pool.tmpl | 15 + .../cloud_loadbalancer_pool_member.tmpl | 14 + .../templates/cloud_loadbalancer_stats.tmpl | 10 + 27 files changed, 2095 insertions(+), 12 deletions(-) create mode 100644 internal/cmd/cloud_network_loadbalancer.go create mode 100644 internal/services/cloud/cloud_loadbalancer_health_monitor.go create mode 100644 internal/services/cloud/cloud_loadbalancer_l7policy.go create mode 100644 internal/services/cloud/cloud_loadbalancer_listener.go create mode 100644 internal/services/cloud/cloud_loadbalancer_log.go create mode 100644 internal/services/cloud/cloud_loadbalancer_pool.go create mode 100644 internal/services/cloud/parameter-samples/loadbalancer-associate-floating-ip.json create mode 100644 internal/services/cloud/parameter-samples/loadbalancer-create-floating-ip.json create mode 100644 internal/services/cloud/parameter-samples/loadbalancer-create.json create mode 100644 internal/services/cloud/parameter-samples/loadbalancer-health-monitor-create.json create mode 100644 internal/services/cloud/parameter-samples/loadbalancer-l7policy-create.json create mode 100644 internal/services/cloud/parameter-samples/loadbalancer-l7rule-create.json create mode 100644 internal/services/cloud/parameter-samples/loadbalancer-listener-create.json create mode 100644 internal/services/cloud/parameter-samples/loadbalancer-log-subscription-create.json create mode 100644 internal/services/cloud/parameter-samples/loadbalancer-pool-create.json create mode 100644 internal/services/cloud/parameter-samples/loadbalancer-pool-member-create.json create mode 100644 internal/services/cloud/templates/cloud_loadbalancer_health_monitor.tmpl create mode 100644 internal/services/cloud/templates/cloud_loadbalancer_l7policy.tmpl create mode 100644 internal/services/cloud/templates/cloud_loadbalancer_l7rule.tmpl create mode 100644 internal/services/cloud/templates/cloud_loadbalancer_listener.tmpl create mode 100644 internal/services/cloud/templates/cloud_loadbalancer_log_subscription.tmpl create mode 100644 internal/services/cloud/templates/cloud_loadbalancer_pool.tmpl create mode 100644 internal/services/cloud/templates/cloud_loadbalancer_pool_member.tmpl create mode 100644 internal/services/cloud/templates/cloud_loadbalancer_stats.tmpl diff --git a/internal/cmd/cloud_network_loadbalancer.go b/internal/cmd/cloud_network_loadbalancer.go new file mode 100644 index 00000000..fdeb2059 --- /dev/null +++ b/internal/cmd/cloud_network_loadbalancer.go @@ -0,0 +1,615 @@ +// SPDX-FileCopyrightText: 2025 OVH SAS +// +// SPDX-License-Identifier: Apache-2.0 + +package cmd + +import ( + "github.com/ovh/ovhcloud-cli/internal/assets" + "github.com/ovh/ovhcloud-cli/internal/services/cloud" + "github.com/spf13/cobra" +) + +func initLoadbalancerSubCommands(loadbalancerCmd *cobra.Command) { + // Loadbalancer create + loadbalancerCmd.AddCommand(getLoadbalancerCreationCmd()) + + // Loadbalancer delete + loadbalancerCmd.AddCommand(&cobra.Command{ + Use: "delete ", + Short: "Delete a specific loadbalancer", + Run: cloud.DeleteCloudLoadbalancer, + Args: cobra.ExactArgs(1), + }) + + // Loadbalancer stats + loadbalancerCmd.AddCommand(&cobra.Command{ + Use: "stats ", + Short: "Get statistics for a loadbalancer", + Run: cloud.GetCloudLoadbalancerStats, + Args: cobra.ExactArgs(1), + }) + + // Associate floating IP + loadbalancerCmd.AddCommand(getLoadbalancerAssociateFloatingIpCmd()) + + // Create floating IP + loadbalancerCmd.AddCommand(getLoadbalancerCreateFloatingIpCmd()) + + // Listener sub-commands + initListenerSubCommands(loadbalancerCmd) + + // Pool sub-commands + initPoolSubCommands(loadbalancerCmd) + + // Health Monitor sub-commands + initHealthMonitorSubCommands(loadbalancerCmd) + + // L7 Policy sub-commands + initL7PolicySubCommands(loadbalancerCmd) + + // Log sub-commands + initLogSubCommands(loadbalancerCmd) +} + +// Loadbalancer creation command helpers + +func getLoadbalancerCreationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create ", + Short: "Create a loadbalancer in the given cloud project", + Long: `Use this command to create a loadbalancer. +There are three ways to define the parameters: + +1. Using a configuration file: + + First you can generate an example of parameters file using the following command: + + ovhcloud cloud network loadbalancer create --init-file ./params.json + + After editing the file to set the correct creation parameters, run: + + ovhcloud cloud network loadbalancer create --from-file ./params.json + + Note that you can also pipe the content of the parameters file. + +2. Using your default text editor: + + ovhcloud cloud network loadbalancer create --editor + +3. Using only CLI flags: + + ovhcloud cloud network loadbalancer create --name my-lb --flavor +`, + Run: cloud.CreateCloudLoadbalancer, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.Name, "name", "", "Name of the loadbalancer") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.FlavorId, "flavor", "", "Flavor ID (can be retrieved with 'cloud reference loadbalancer list-flavors ')") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer", "post", cloud.LoadbalancerCreationExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +func getLoadbalancerAssociateFloatingIpCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "associate-floating-ip ", + Short: "Associate an existing floating IP to a loadbalancer", + Run: cloud.AssociateFloatingIpToLoadbalancer, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().StringVar(&cloud.CloudLoadbalancerAssociateFloatingIpSpec.FloatingIpId, "floating-ip-id", "", "Floating IP ID") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerAssociateFloatingIpSpec.Ip, "ip", "", "Private loadbalancer IP to associate the floating IP with") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer/{loadBalancerId}/associateFloatingIp", "post", cloud.LoadbalancerAssociateFloatingIpExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +func getLoadbalancerCreateFloatingIpCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create-floating-ip ", + Short: "Create a floating IP and attach it to a loadbalancer", + Run: cloud.CreateFloatingIpForLoadbalancer, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateFloatingIpSpec.Ip, "ip", "", "Private loadbalancer IP to associate the floating IP with") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer/{loadBalancerId}/floatingIp", "post", cloud.LoadbalancerCreateFloatingIpExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +// Listener sub-commands + +func initListenerSubCommands(loadbalancerCmd *cobra.Command) { + listenerCmd := &cobra.Command{ + Use: "listener", + Short: "Manage listeners of loadbalancers", + } + loadbalancerCmd.AddCommand(listenerCmd) + + listenerCmd.AddCommand(withFilterFlag(&cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "List all listeners", + Run: cloud.ListCloudLoadbalancerListeners, + })) + + listenerCmd.AddCommand(&cobra.Command{ + Use: "get ", + Short: "Get a specific listener", + Run: cloud.GetCloudLoadbalancerListener, + Args: cobra.ExactArgs(1), + }) + + listenerCmd.AddCommand(getListenerCreationCmd()) + + listenerEditCmd := &cobra.Command{ + Use: "edit ", + Short: "Edit a specific listener", + Run: cloud.EditCloudLoadbalancerListener, + Args: cobra.ExactArgs(1), + } + listenerEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerUpdateSpec.Name, "name", "", "Name of the listener") + listenerEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerUpdateSpec.Description, "description", "", "Description of the listener") + listenerEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerUpdateSpec.DefaultPoolId, "default-pool-id", "", "Default pool ID") + listenerEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerUpdateSpec.CertificateId, "certificate-id", "", "Certificate ID") + addInteractiveEditorFlag(listenerEditCmd) + listenerCmd.AddCommand(listenerEditCmd) + + listenerCmd.AddCommand(&cobra.Command{ + Use: "delete ", + Short: "Delete a specific listener", + Run: cloud.DeleteCloudLoadbalancerListener, + Args: cobra.ExactArgs(1), + }) +} + +func getListenerCreationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create ", + Short: "Create a listener in the given region", + Run: cloud.CreateCloudLoadbalancerListener, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerCreateSpec.LoadbalancerId, "loadbalancer-id", "", "Loadbalancer ID") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerCreateSpec.Name, "name", "", "Name of the listener") + cmd.Flags().IntVar(&cloud.CloudLoadbalancerListenerCreateSpec.Port, "port", 0, "Port to listen on") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerCreateSpec.Protocol, "protocol", "", "Protocol (http, https, tcp, udp, sctp, prometheus)") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/listener", "post", cloud.LoadbalancerListenerCreationExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +// Pool sub-commands + +func initPoolSubCommands(loadbalancerCmd *cobra.Command) { + poolCmd := &cobra.Command{ + Use: "pool", + Short: "Manage pools of loadbalancers", + } + loadbalancerCmd.AddCommand(poolCmd) + + poolCmd.AddCommand(withFilterFlag(&cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "List all pools", + Run: cloud.ListCloudLoadbalancerPools, + })) + + poolCmd.AddCommand(&cobra.Command{ + Use: "get ", + Short: "Get a specific pool", + Run: cloud.GetCloudLoadbalancerPool, + Args: cobra.ExactArgs(1), + }) + + poolCmd.AddCommand(getPoolCreationCmd()) + + poolEditCmd := &cobra.Command{ + Use: "edit ", + Short: "Edit a specific pool", + Run: cloud.EditCloudLoadbalancerPool, + Args: cobra.ExactArgs(1), + } + poolEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolUpdateSpec.Algorithm, "algorithm", "", "Algorithm (roundRobin, leastConnections, sourceIp)") + poolEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolUpdateSpec.Name, "name", "", "Name of the pool") + addInteractiveEditorFlag(poolEditCmd) + poolCmd.AddCommand(poolEditCmd) + + poolCmd.AddCommand(&cobra.Command{ + Use: "delete ", + Short: "Delete a specific pool", + Run: cloud.DeleteCloudLoadbalancerPool, + Args: cobra.ExactArgs(1), + }) + + // Pool Member sub-commands + initPoolMemberSubCommands(poolCmd) +} + +func getPoolCreationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create ", + Short: "Create a pool in the given region", + Run: cloud.CreateCloudLoadbalancerPool, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolCreateSpec.Algorithm, "algorithm", "", "Algorithm (roundRobin, leastConnections, sourceIp)") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolCreateSpec.ListenerId, "listener-id", "", "Listener ID") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolCreateSpec.LoadbalancerId, "loadbalancer-id", "", "Loadbalancer ID") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolCreateSpec.Name, "name", "", "Name of the pool") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolCreateSpec.Protocol, "protocol", "", "Protocol (http, https, tcp, udp, sctp, prometheus)") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/pool", "post", cloud.LoadbalancerPoolCreationExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +func initPoolMemberSubCommands(poolCmd *cobra.Command) { + memberCmd := &cobra.Command{ + Use: "member", + Short: "Manage members of a loadbalancer pool", + } + poolCmd.AddCommand(memberCmd) + + memberCmd.AddCommand(withFilterFlag(&cobra.Command{ + Use: "list ", + Aliases: []string{"ls"}, + Short: "List members of a specific pool", + Run: cloud.ListCloudLoadbalancerPoolMembers, + Args: cobra.ExactArgs(1), + })) + + memberCmd.AddCommand(&cobra.Command{ + Use: "get ", + Short: "Get a specific pool member", + Run: cloud.GetCloudLoadbalancerPoolMember, + Args: cobra.ExactArgs(2), + }) + + memberCmd.AddCommand(getPoolMemberCreationCmd()) + + memberEditCmd := &cobra.Command{ + Use: "edit ", + Short: "Edit a specific pool member", + Run: cloud.EditCloudLoadbalancerPoolMember, + Args: cobra.ExactArgs(2), + } + memberEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolMemberUpdateSpec.Name, "name", "", "Name of the member") + memberEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerPoolMemberUpdateSpec.Weight, "weight", 0, "Weight of the member (1-256)") + addInteractiveEditorFlag(memberEditCmd) + memberCmd.AddCommand(memberEditCmd) + + memberCmd.AddCommand(&cobra.Command{ + Use: "delete ", + Short: "Delete a specific pool member", + Run: cloud.DeleteCloudLoadbalancerPoolMember, + Args: cobra.ExactArgs(2), + }) +} + +func getPoolMemberCreationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create ", + Short: "Create member(s) in a specific pool", + Run: cloud.CreateCloudLoadbalancerPoolMember, + Args: cobra.ExactArgs(1), + } + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/pool/{poolId}/member", "post", cloud.LoadbalancerPoolMemberCreationExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +// Health Monitor sub-commands + +func initHealthMonitorSubCommands(loadbalancerCmd *cobra.Command) { + healthMonitorCmd := &cobra.Command{ + Use: "health-monitor", + Short: "Manage health monitors of loadbalancers", + } + loadbalancerCmd.AddCommand(healthMonitorCmd) + + healthMonitorCmd.AddCommand(withFilterFlag(&cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "List all health monitors", + Run: cloud.ListCloudLoadbalancerHealthMonitors, + })) + + healthMonitorCmd.AddCommand(&cobra.Command{ + Use: "get ", + Short: "Get a specific health monitor", + Run: cloud.GetCloudLoadbalancerHealthMonitor, + Args: cobra.ExactArgs(1), + }) + + healthMonitorCmd.AddCommand(getHealthMonitorCreationCmd()) + + healthMonitorEditCmd := &cobra.Command{ + Use: "edit ", + Short: "Edit a specific health monitor", + Run: cloud.EditCloudLoadbalancerHealthMonitor, + Args: cobra.ExactArgs(1), + } + healthMonitorEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorUpdateSpec.Delay, "delay", 0, "Duration between sending probes to members, in seconds") + healthMonitorEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorUpdateSpec.MaxRetries, "max-retries", 0, "Number of successful checks before changing status to ONLINE") + healthMonitorEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorUpdateSpec.MaxRetriesDown, "max-retries-down", 0, "Number of allowed check failures before changing status to ERROR") + healthMonitorEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerHealthMonitorUpdateSpec.Name, "name", "", "Name of the health monitor") + healthMonitorEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorUpdateSpec.Timeout, "timeout", 0, "Maximum time in seconds to connect before timeout") + addInteractiveEditorFlag(healthMonitorEditCmd) + healthMonitorCmd.AddCommand(healthMonitorEditCmd) + + healthMonitorCmd.AddCommand(&cobra.Command{ + Use: "delete ", + Short: "Delete a specific health monitor", + Run: cloud.DeleteCloudLoadbalancerHealthMonitor, + Args: cobra.ExactArgs(1), + }) +} + +func getHealthMonitorCreationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create ", + Short: "Create a health monitor in the given region", + Run: cloud.CreateCloudLoadbalancerHealthMonitor, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.Delay, "delay", 0, "Duration between sending probes to members, in seconds") + cmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.MaxRetries, "max-retries", 0, "Number of successful checks before changing status to ONLINE") + cmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.MaxRetriesDown, "max-retries-down", 0, "Number of allowed check failures before changing status to ERROR") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.MonitorType, "monitor-type", "", "Type of the monitor (http, https, ping, tcp, tls-hello, udp-connect, sctp)") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.Name, "name", "", "Name of the health monitor") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.PoolId, "pool-id", "", "Pool ID") + cmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.Timeout, "timeout", 0, "Maximum time in seconds to connect before timeout") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/healthMonitor", "post", cloud.LoadbalancerHealthMonitorCreationExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +// L7 Policy sub-commands + +func initL7PolicySubCommands(loadbalancerCmd *cobra.Command) { + l7PolicyCmd := &cobra.Command{ + Use: "l7policy", + Short: "Manage L7 policies of loadbalancers", + } + loadbalancerCmd.AddCommand(l7PolicyCmd) + + l7PolicyCmd.AddCommand(withFilterFlag(&cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "List all L7 policies", + Run: cloud.ListCloudLoadbalancerL7Policies, + })) + + l7PolicyCmd.AddCommand(&cobra.Command{ + Use: "get ", + Short: "Get a specific L7 policy", + Run: cloud.GetCloudLoadbalancerL7Policy, + Args: cobra.ExactArgs(1), + }) + + l7PolicyCmd.AddCommand(getL7PolicyCreationCmd()) + + l7PolicyEditCmd := &cobra.Command{ + Use: "edit ", + Short: "Edit a specific L7 policy", + Run: cloud.EditCloudLoadbalancerL7Policy, + Args: cobra.ExactArgs(1), + } + l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.Action, "action", "", "L7 policy action") + l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.Description, "description", "", "Description") + l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.ListenerId, "listener-id", "", "Listener ID") + l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.Name, "name", "", "Name of the L7 policy") + l7PolicyEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.Position, "position", 0, "Position on the listener") + l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.RedirectPoolId, "redirect-pool-id", "", "Redirect pool ID") + l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.RedirectPrefix, "redirect-prefix", "", "Redirect prefix URL") + l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.RedirectUrl, "redirect-url", "", "Redirect URL") + addInteractiveEditorFlag(l7PolicyEditCmd) + l7PolicyCmd.AddCommand(l7PolicyEditCmd) + + l7PolicyCmd.AddCommand(&cobra.Command{ + Use: "delete ", + Short: "Delete a specific L7 policy", + Run: cloud.DeleteCloudLoadbalancerL7Policy, + Args: cobra.ExactArgs(1), + }) + + // L7 Rule sub-commands + initL7RuleSubCommands(l7PolicyCmd) +} + +func getL7PolicyCreationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create ", + Short: "Create an L7 policy in the given region", + Run: cloud.CreateCloudLoadbalancerL7Policy, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.Action, "action", "", "L7 policy action (redirectToPool, redirectToUrl, redirectPrefix, reject)") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.Description, "description", "", "Description") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.ListenerId, "listener-id", "", "Listener ID") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.Name, "name", "", "Name of the L7 policy") + cmd.Flags().IntVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.Position, "position", 0, "Position on the listener") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.RedirectPoolId, "redirect-pool-id", "", "Redirect pool ID") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.RedirectPrefix, "redirect-prefix", "", "Redirect prefix URL") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.RedirectUrl, "redirect-url", "", "Redirect URL") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/l7Policy", "post", cloud.LoadbalancerL7PolicyCreationExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +func initL7RuleSubCommands(l7PolicyCmd *cobra.Command) { + l7RuleCmd := &cobra.Command{ + Use: "l7rule", + Short: "Manage L7 rules of a loadbalancer L7 policy", + } + l7PolicyCmd.AddCommand(l7RuleCmd) + + l7RuleCmd.AddCommand(withFilterFlag(&cobra.Command{ + Use: "list ", + Aliases: []string{"ls"}, + Short: "List L7 rules of a specific L7 policy", + Run: cloud.ListCloudLoadbalancerL7Rules, + Args: cobra.ExactArgs(1), + })) + + l7RuleCmd.AddCommand(&cobra.Command{ + Use: "get ", + Short: "Get a specific L7 rule", + Run: cloud.GetCloudLoadbalancerL7Rule, + Args: cobra.ExactArgs(2), + }) + + l7RuleCmd.AddCommand(getL7RuleCreationCmd()) + + l7RuleEditCmd := &cobra.Command{ + Use: "edit ", + Short: "Edit a specific L7 rule", + Run: cloud.EditCloudLoadbalancerL7Rule, + Args: cobra.ExactArgs(2), + } + l7RuleEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleUpdateSpec.CompareType, "compare-type", "", "Comparison type") + l7RuleEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleUpdateSpec.Key, "key", "", "Key to use for comparison") + l7RuleEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleUpdateSpec.RuleType, "rule-type", "", "Rule type") + l7RuleEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleUpdateSpec.Value, "value", "", "Value to compare") + addInteractiveEditorFlag(l7RuleEditCmd) + l7RuleCmd.AddCommand(l7RuleEditCmd) + + l7RuleCmd.AddCommand(&cobra.Command{ + Use: "delete ", + Short: "Delete a specific L7 rule", + Run: cloud.DeleteCloudLoadbalancerL7Rule, + Args: cobra.ExactArgs(2), + }) +} + +func getL7RuleCreationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create ", + Short: "Create an L7 rule in a specific L7 policy", + Run: cloud.CreateCloudLoadbalancerL7Rule, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleCreateSpec.CompareType, "compare-type", "", "Comparison type (contains, endsWith, equalTo, regex, startsWith)") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleCreateSpec.Key, "key", "", "Key to use for comparison") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleCreateSpec.RuleType, "rule-type", "", "Rule type (cookie, fileType, header, hostName, path, sslConnHasCert, sslDNField, sslVerifyResult)") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleCreateSpec.Value, "value", "", "Value to compare") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/l7Policy/{l7PolicyId}/l7Rule", "post", cloud.LoadbalancerL7RuleCreationExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +// Log sub-commands + +func initLogSubCommands(loadbalancerCmd *cobra.Command) { + logCmd := &cobra.Command{ + Use: "log", + Short: "Manage loadbalancer logs", + } + loadbalancerCmd.AddCommand(logCmd) + + logCmd.AddCommand(withFilterFlag(&cobra.Command{ + Use: "list-kinds ", + Short: "List available log kinds", + Run: cloud.ListCloudLoadbalancerLogKinds, + Args: cobra.ExactArgs(1), + })) + + logCmd.AddCommand(&cobra.Command{ + Use: "get-kind ", + Short: "Get a specific log kind", + Run: cloud.GetCloudLoadbalancerLogKind, + Args: cobra.ExactArgs(2), + }) + + logCmd.AddCommand(&cobra.Command{ + Use: "generate-url ", + Short: "Generate a temporary URL to retrieve logs", + Run: cloud.GenerateCloudLoadbalancerLogURL, + Args: cobra.ExactArgs(1), + }) + + // Log Subscription sub-commands + subscriptionCmd := &cobra.Command{ + Use: "subscription", + Short: "Manage log subscriptions for a loadbalancer", + } + logCmd.AddCommand(subscriptionCmd) + + subscriptionCmd.AddCommand(withFilterFlag(&cobra.Command{ + Use: "list ", + Aliases: []string{"ls"}, + Short: "List log subscriptions of a loadbalancer", + Run: cloud.ListCloudLoadbalancerLogSubscriptions, + Args: cobra.ExactArgs(1), + })) + + subscriptionCmd.AddCommand(&cobra.Command{ + Use: "get ", + Short: "Get a specific log subscription", + Run: cloud.GetCloudLoadbalancerLogSubscription, + Args: cobra.ExactArgs(2), + }) + + subscriptionCmd.AddCommand(getLogSubscriptionCreationCmd()) + + subscriptionCmd.AddCommand(&cobra.Command{ + Use: "delete ", + Short: "Delete a log subscription", + Run: cloud.DeleteCloudLoadbalancerLogSubscription, + Args: cobra.ExactArgs(2), + }) +} + +func getLogSubscriptionCreationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create ", + Short: "Create a log subscription for a loadbalancer", + Run: cloud.CreateCloudLoadbalancerLogSubscription, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().StringVar(&cloud.CloudLoadbalancerLogSubscriptionCreateSpec.Kind, "kind", "", "Log kind (e.g., haproxy)") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerLogSubscriptionCreateSpec.StreamId, "stream-id", "", "LDP stream ID") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer/{loadBalancerId}/log/subscription", "post", cloud.LoadbalancerLogSubscriptionCreationExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} diff --git a/internal/cmd/cloud_reference.go b/internal/cmd/cloud_reference.go index e125386e..ecd48bdf 100644 --- a/internal/cmd/cloud_reference.go +++ b/internal/cmd/cloud_reference.go @@ -132,5 +132,12 @@ func initCloudReferenceCmd(cloudCmd *cobra.Command) { Args: cobra.ExactArgs(1), })) + loadbalancerReferenceCmd.AddCommand(&cobra.Command{ + Use: "get-flavor ", + Short: "Get details of a specific loadbalancer flavor", + Run: cloud.GetLoadbalancerFlavor, + Args: cobra.ExactArgs(2), + }) + cloudCmd.AddCommand(referenceCmd) } diff --git a/internal/services/cloud/cloud_loadbalancer.go b/internal/services/cloud/cloud_loadbalancer.go index 9f27259d..4cbbe906 100644 --- a/internal/services/cloud/cloud_loadbalancer.go +++ b/internal/services/cloud/cloud_loadbalancer.go @@ -24,11 +24,37 @@ var ( //go:embed templates/cloud_loadbalancer.tmpl cloudLoadbalancerTemplate string + //go:embed templates/cloud_loadbalancer_stats.tmpl + cloudLoadbalancerStatsTemplate string + + //go:embed parameter-samples/loadbalancer-create.json + LoadbalancerCreationExample string + + //go:embed parameter-samples/loadbalancer-associate-floating-ip.json + LoadbalancerAssociateFloatingIpExample string + + //go:embed parameter-samples/loadbalancer-create-floating-ip.json + LoadbalancerCreateFloatingIpExample string + CloudLoadbalancerUpdateSpec struct { Description string `json:"description,omitempty"` Name string `json:"name,omitempty"` FlavorId string `json:"flavorId,omitempty"` } + + CloudLoadbalancerCreateSpec struct { + FlavorId string `json:"flavorId,omitempty"` + Name string `json:"name,omitempty"` + } + + CloudLoadbalancerAssociateFloatingIpSpec struct { + FloatingIpId string `json:"floatingIpId,omitempty"` + Ip string `json:"ip,omitempty"` + } + + CloudLoadbalancerCreateFloatingIpSpec struct { + Ip string `json:"ip,omitempty"` + } ) func locateLoadbalancer(projectID, loadbalancerID string) (string, map[string]any, error) { @@ -52,42 +78,64 @@ func locateLoadbalancer(projectID, loadbalancerID string) (string, map[string]an return "", nil, fmt.Errorf("no loadbalancer found with id %s", loadbalancerID) } -func ListCloudLoadbalancers(_ *cobra.Command, _ []string) { +// locateLoadbalancingResource searches for a loadbalancing resource by type and ID across all regions. +// resourceType is e.g. "listener", "pool", "healthMonitor", "l7Policy". +func locateLoadbalancingResource(projectID, resourceType, resourceID string) (string, map[string]any, error) { + regions, err := getCloudRegionsWithFeatureAvailable(projectID, "octavialoadbalancer") + if err != nil { + return "", nil, fmt.Errorf("failed to fetch regions with loadbalancer feature available: %w", err) + } + + for _, region := range regions { + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/%s/%s", + projectID, url.PathEscape(region.(string)), resourceType, url.PathEscape(resourceID)) + + var resource map[string]any + if err := httpLib.Client.Get(endpoint, &resource); err == nil { + return region.(string), resource, nil + } + } + + return "", nil, fmt.Errorf("no %s found with id %s", resourceType, resourceID) +} + +// listLoadbalancingResources fetches a loadbalancing resource type across all regions in parallel. +func listLoadbalancingResources(resourceType string, columnsToDisplay []string) { projectID, err := getConfiguredCloudProject() if err != nil { display.OutputError(&flags.OutputFormatConfig, "%s", err) return } - // Fetch regions with loadbalancer feature available regions, err := getCloudRegionsWithFeatureAvailable(projectID, "octavialoadbalancer") if err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to fetch regions with loadbalancer feature available: %s", err) return } - // Fetch loadbalancers in all regions endpoint := fmt.Sprintf("/v1/cloud/project/%s/region", projectID) - containers, err := httpLib.FetchObjectsParallel[[]map[string]any](endpoint+"/%s/loadbalancing/loadbalancer", regions, true) + containers, err := httpLib.FetchObjectsParallel[[]map[string]any](endpoint+"/%s/loadbalancing/"+resourceType, regions, true) if err != nil { - display.OutputError(&flags.OutputFormatConfig, "failed to fetch loadbalancers: %s", err) + display.OutputError(&flags.OutputFormatConfig, "failed to fetch %s: %s", resourceType, err) return } - // Flatten loadbalancers in a single array - var allLoadbalancers []map[string]any - for _, regionLoadbalancers := range containers { - allLoadbalancers = append(allLoadbalancers, regionLoadbalancers...) + var allResources []map[string]any + for _, regionResources := range containers { + allResources = append(allResources, regionResources...) } - // Filter results - allLoadbalancers, err = filtersLib.FilterLines(allLoadbalancers, flags.GenericFilters) + allResources, err = filtersLib.FilterLines(allResources, flags.GenericFilters) if err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to filter results: %s", err) return } - display.RenderTable(allLoadbalancers, cloudprojectLoadbalancerColumnsToDisplay, &flags.OutputFormatConfig) + display.RenderTable(allResources, columnsToDisplay, &flags.OutputFormatConfig) +} + +func ListCloudLoadbalancers(_ *cobra.Command, _ []string) { + listLoadbalancingResources("loadbalancer", cloudprojectLoadbalancerColumnsToDisplay) } func GetCloudLoadbalancer(_ *cobra.Command, args []string) { @@ -142,3 +190,146 @@ func EditCloudLoadbalancer(cmd *cobra.Command, args []string) { return } } + +func CreateCloudLoadbalancer(cmd *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region := args[0] + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/loadbalancer", + projectID, url.PathEscape(region)) + + result, err := common.CreateResource( + cmd, + "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer", + endpoint, + LoadbalancerCreationExample, + CloudLoadbalancerCreateSpec, + assets.CloudOpenapiSchema, + nil, + ) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to create loadbalancer: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, result, "✅ Loadbalancer created successfully (ID: %s)", result["id"]) +} + +func DeleteCloudLoadbalancer(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancer(projectID, args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s", + projectID, url.PathEscape(region), url.PathEscape(args[0])) + + if err := httpLib.Client.Delete(endpoint, nil); err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to delete loadbalancer: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ Loadbalancer %s deleted successfully", args[0]) +} + +func GetCloudLoadbalancerStats(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancer(projectID, args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s/stats", + projectID, url.PathEscape(region), url.PathEscape(args[0])) + + var stats map[string]any + if err := httpLib.Client.Get(endpoint, &stats); err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to fetch loadbalancer stats: %s", err) + return + } + + display.OutputObject(stats, args[0], cloudLoadbalancerStatsTemplate, &flags.OutputFormatConfig) +} + +func AssociateFloatingIpToLoadbalancer(cmd *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancer(projectID, args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s/associateFloatingIp", + projectID, url.PathEscape(region), url.PathEscape(args[0])) + + result, err := common.CreateResource( + cmd, + "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer/{loadBalancerId}/associateFloatingIp", + endpoint, + LoadbalancerAssociateFloatingIpExample, + CloudLoadbalancerAssociateFloatingIpSpec, + assets.CloudOpenapiSchema, + nil, + ) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to associate floating IP: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, result, "✅ Floating IP associated to loadbalancer %s successfully", args[0]) +} + +func CreateFloatingIpForLoadbalancer(cmd *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancer(projectID, args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s/floatingIp", + projectID, url.PathEscape(region), url.PathEscape(args[0])) + + result, err := common.CreateResource( + cmd, + "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer/{loadBalancerId}/floatingIp", + endpoint, + LoadbalancerCreateFloatingIpExample, + CloudLoadbalancerCreateFloatingIpSpec, + assets.CloudOpenapiSchema, + nil, + ) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to create floating IP: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, result, "✅ Floating IP created and attached to loadbalancer %s successfully", args[0]) +} diff --git a/internal/services/cloud/cloud_loadbalancer_health_monitor.go b/internal/services/cloud/cloud_loadbalancer_health_monitor.go new file mode 100644 index 00000000..baedf5c5 --- /dev/null +++ b/internal/services/cloud/cloud_loadbalancer_health_monitor.go @@ -0,0 +1,144 @@ +// SPDX-FileCopyrightText: 2025 OVH SAS +// +// SPDX-License-Identifier: Apache-2.0 + +package cloud + +import ( + _ "embed" + "fmt" + "net/url" + + "github.com/ovh/ovhcloud-cli/internal/assets" + "github.com/ovh/ovhcloud-cli/internal/display" + "github.com/ovh/ovhcloud-cli/internal/flags" + httpLib "github.com/ovh/ovhcloud-cli/internal/http" + "github.com/ovh/ovhcloud-cli/internal/services/common" + "github.com/spf13/cobra" +) + +var ( + healthMonitorColumnsToDisplay = []string{"id", "name", "monitorType", "poolId", "operatingStatus", "provisioningStatus"} + + //go:embed templates/cloud_loadbalancer_health_monitor.tmpl + cloudLoadbalancerHealthMonitorTemplate string + + //go:embed parameter-samples/loadbalancer-health-monitor-create.json + LoadbalancerHealthMonitorCreationExample string + + CloudLoadbalancerHealthMonitorCreateSpec struct { + Delay int `json:"delay,omitempty"` + MaxRetries int `json:"maxRetries,omitempty"` + MaxRetriesDown int `json:"maxRetriesDown,omitempty"` + MonitorType string `json:"monitorType,omitempty"` + Name string `json:"name,omitempty"` + PoolId string `json:"poolId,omitempty"` + Timeout int `json:"timeout,omitempty"` + } + + CloudLoadbalancerHealthMonitorUpdateSpec struct { + Delay int `json:"delay,omitempty"` + MaxRetries int `json:"maxRetries,omitempty"` + MaxRetriesDown int `json:"maxRetriesDown,omitempty"` + Name string `json:"name,omitempty"` + Timeout int `json:"timeout,omitempty"` + } +) + +func ListCloudLoadbalancerHealthMonitors(_ *cobra.Command, _ []string) { + listLoadbalancingResources("healthMonitor", healthMonitorColumnsToDisplay) +} + +func GetCloudLoadbalancerHealthMonitor(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + _, hm, err := locateLoadbalancingResource(projectID, "healthMonitor", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + display.OutputObject(hm, args[0], cloudLoadbalancerHealthMonitorTemplate, &flags.OutputFormatConfig) +} + +func CreateCloudLoadbalancerHealthMonitor(cmd *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region := args[0] + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/healthMonitor", + projectID, url.PathEscape(region)) + + result, err := common.CreateResource( + cmd, + "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/healthMonitor", + endpoint, + LoadbalancerHealthMonitorCreationExample, + CloudLoadbalancerHealthMonitorCreateSpec, + assets.CloudOpenapiSchema, + nil, + ) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to create health monitor: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, result, "✅ Health monitor created successfully (ID: %s)", result["id"]) +} + +func EditCloudLoadbalancerHealthMonitor(cmd *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "healthMonitor", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + if err := common.EditResource( + cmd, + "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/healthMonitor/{healthMonitorId}", + fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/healthMonitor/%s", + projectID, url.PathEscape(region), url.PathEscape(args[0])), + CloudLoadbalancerHealthMonitorUpdateSpec, + assets.CloudOpenapiSchema, + ); err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } +} + +func DeleteCloudLoadbalancerHealthMonitor(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "healthMonitor", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/healthMonitor/%s", + projectID, url.PathEscape(region), url.PathEscape(args[0])) + + if err := httpLib.Client.Delete(endpoint, nil); err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to delete health monitor: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ Health monitor %s deleted successfully", args[0]) +} diff --git a/internal/services/cloud/cloud_loadbalancer_l7policy.go b/internal/services/cloud/cloud_loadbalancer_l7policy.go new file mode 100644 index 00000000..f424803f --- /dev/null +++ b/internal/services/cloud/cloud_loadbalancer_l7policy.go @@ -0,0 +1,304 @@ +// SPDX-FileCopyrightText: 2025 OVH SAS +// +// SPDX-License-Identifier: Apache-2.0 + +package cloud + +import ( + _ "embed" + "fmt" + "net/url" + + "github.com/ovh/ovhcloud-cli/internal/assets" + "github.com/ovh/ovhcloud-cli/internal/display" + "github.com/ovh/ovhcloud-cli/internal/flags" + httpLib "github.com/ovh/ovhcloud-cli/internal/http" + "github.com/ovh/ovhcloud-cli/internal/services/common" + "github.com/spf13/cobra" +) + +var ( + l7PolicyColumnsToDisplay = []string{"id", "name", "action", "listenerId", "position", "operatingStatus"} + l7RuleColumnsToDisplay = []string{"id", "ruleType", "compareType", "value", "key", "invert"} + + //go:embed templates/cloud_loadbalancer_l7policy.tmpl + cloudLoadbalancerL7PolicyTemplate string + + //go:embed templates/cloud_loadbalancer_l7rule.tmpl + cloudLoadbalancerL7RuleTemplate string + + //go:embed parameter-samples/loadbalancer-l7policy-create.json + LoadbalancerL7PolicyCreationExample string + + //go:embed parameter-samples/loadbalancer-l7rule-create.json + LoadbalancerL7RuleCreationExample string + + CloudLoadbalancerL7PolicyCreateSpec struct { + Action string `json:"action,omitempty"` + Description string `json:"description,omitempty"` + ListenerId string `json:"listenerId,omitempty"` + Name string `json:"name,omitempty"` + Position int `json:"position,omitempty"` + RedirectHttpCode int `json:"redirectHttpCode,omitempty"` + RedirectPoolId string `json:"redirectPoolId,omitempty"` + RedirectPrefix string `json:"redirectPrefix,omitempty"` + RedirectUrl string `json:"redirectUrl,omitempty"` + } + + CloudLoadbalancerL7PolicyUpdateSpec struct { + Action string `json:"action,omitempty"` + Description string `json:"description,omitempty"` + ListenerId string `json:"listenerId,omitempty"` + Name string `json:"name,omitempty"` + Position int `json:"position,omitempty"` + RedirectHttpCode int `json:"redirectHttpCode,omitempty"` + RedirectPoolId string `json:"redirectPoolId,omitempty"` + RedirectPrefix string `json:"redirectPrefix,omitempty"` + RedirectUrl string `json:"redirectUrl,omitempty"` + } + + CloudLoadbalancerL7RuleCreateSpec struct { + CompareType string `json:"compareType,omitempty"` + Invert bool `json:"invert,omitempty"` + Key string `json:"key,omitempty"` + RuleType string `json:"ruleType,omitempty"` + Value string `json:"value,omitempty"` + } + + CloudLoadbalancerL7RuleUpdateSpec struct { + CompareType string `json:"compareType,omitempty"` + Invert bool `json:"invert,omitempty"` + Key string `json:"key,omitempty"` + RuleType string `json:"ruleType,omitempty"` + Value string `json:"value,omitempty"` + } +) + +// L7 Policy operations + +func ListCloudLoadbalancerL7Policies(_ *cobra.Command, _ []string) { + listLoadbalancingResources("l7Policy", l7PolicyColumnsToDisplay) +} + +func GetCloudLoadbalancerL7Policy(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + _, policy, err := locateLoadbalancingResource(projectID, "l7Policy", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + display.OutputObject(policy, args[0], cloudLoadbalancerL7PolicyTemplate, &flags.OutputFormatConfig) +} + +func CreateCloudLoadbalancerL7Policy(cmd *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region := args[0] + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/l7Policy", + projectID, url.PathEscape(region)) + + result, err := common.CreateResource( + cmd, + "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/l7Policy", + endpoint, + LoadbalancerL7PolicyCreationExample, + CloudLoadbalancerL7PolicyCreateSpec, + assets.CloudOpenapiSchema, + nil, + ) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to create L7 policy: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, result, "✅ L7 policy created successfully (ID: %s)", result["id"]) +} + +func EditCloudLoadbalancerL7Policy(cmd *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "l7Policy", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + if err := common.EditResource( + cmd, + "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/l7Policy/{l7PolicyId}", + fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/l7Policy/%s", + projectID, url.PathEscape(region), url.PathEscape(args[0])), + CloudLoadbalancerL7PolicyUpdateSpec, + assets.CloudOpenapiSchema, + ); err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } +} + +func DeleteCloudLoadbalancerL7Policy(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "l7Policy", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/l7Policy/%s", + projectID, url.PathEscape(region), url.PathEscape(args[0])) + + if err := httpLib.Client.Delete(endpoint, nil); err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to delete L7 policy: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ L7 policy %s deleted successfully", args[0]) +} + +// L7 Rule operations + +func ListCloudLoadbalancerL7Rules(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "l7Policy", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/l7Policy/%s/l7Rule", + projectID, url.PathEscape(region), url.PathEscape(args[0])) + + common.ManageListRequestNoExpand(endpoint, l7RuleColumnsToDisplay, flags.GenericFilters) +} + +func GetCloudLoadbalancerL7Rule(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "l7Policy", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/l7Policy/%s/l7Rule/%s", + projectID, url.PathEscape(region), url.PathEscape(args[0]), url.PathEscape(args[1])) + + var rule map[string]any + if err := httpLib.Client.Get(endpoint, &rule); err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to fetch L7 rule: %s", err) + return + } + + display.OutputObject(rule, args[1], cloudLoadbalancerL7RuleTemplate, &flags.OutputFormatConfig) +} + +func CreateCloudLoadbalancerL7Rule(cmd *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "l7Policy", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/l7Policy/%s/l7Rule", + projectID, url.PathEscape(region), url.PathEscape(args[0])) + + result, err := common.CreateResource( + cmd, + "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/l7Policy/{l7PolicyId}/l7Rule", + endpoint, + LoadbalancerL7RuleCreationExample, + CloudLoadbalancerL7RuleCreateSpec, + assets.CloudOpenapiSchema, + nil, + ) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to create L7 rule: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, result, "✅ L7 rule created successfully (ID: %s)", result["id"]) +} + +func EditCloudLoadbalancerL7Rule(cmd *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "l7Policy", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + if err := common.EditResource( + cmd, + "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/l7Policy/{l7PolicyId}/l7Rule/{l7RuleId}", + fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/l7Policy/%s/l7Rule/%s", + projectID, url.PathEscape(region), url.PathEscape(args[0]), url.PathEscape(args[1])), + CloudLoadbalancerL7RuleUpdateSpec, + assets.CloudOpenapiSchema, + ); err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } +} + +func DeleteCloudLoadbalancerL7Rule(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "l7Policy", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/l7Policy/%s/l7Rule/%s", + projectID, url.PathEscape(region), url.PathEscape(args[0]), url.PathEscape(args[1])) + + if err := httpLib.Client.Delete(endpoint, nil); err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to delete L7 rule: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ L7 rule %s deleted successfully", args[1]) +} diff --git a/internal/services/cloud/cloud_loadbalancer_listener.go b/internal/services/cloud/cloud_loadbalancer_listener.go new file mode 100644 index 00000000..a39de8dd --- /dev/null +++ b/internal/services/cloud/cloud_loadbalancer_listener.go @@ -0,0 +1,141 @@ +// SPDX-FileCopyrightText: 2025 OVH SAS +// +// SPDX-License-Identifier: Apache-2.0 + +package cloud + +import ( + _ "embed" + "fmt" + "net/url" + + "github.com/ovh/ovhcloud-cli/internal/assets" + "github.com/ovh/ovhcloud-cli/internal/display" + "github.com/ovh/ovhcloud-cli/internal/flags" + httpLib "github.com/ovh/ovhcloud-cli/internal/http" + "github.com/ovh/ovhcloud-cli/internal/services/common" + "github.com/spf13/cobra" +) + +var ( + listenerColumnsToDisplay = []string{"id", "name", "protocol", "port", "operatingStatus", "provisioningStatus"} + + //go:embed templates/cloud_loadbalancer_listener.tmpl + cloudLoadbalancerListenerTemplate string + + //go:embed parameter-samples/loadbalancer-listener-create.json + LoadbalancerListenerCreationExample string + + CloudLoadbalancerListenerUpdateSpec struct { + AllowedCidrs []string `json:"allowedCidrs,omitempty"` + CertificateId string `json:"certificateId,omitempty"` + DefaultPoolId string `json:"defaultPoolId,omitempty"` + Description string `json:"description,omitempty"` + Name string `json:"name,omitempty"` + } + + CloudLoadbalancerListenerCreateSpec struct { + LoadbalancerId string `json:"loadbalancerId,omitempty"` + Name string `json:"name,omitempty"` + Port int `json:"port,omitempty"` + Protocol string `json:"protocol,omitempty"` + } +) + +func ListCloudLoadbalancerListeners(_ *cobra.Command, _ []string) { + listLoadbalancingResources("listener", listenerColumnsToDisplay) +} + +func GetCloudLoadbalancerListener(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + _, listener, err := locateLoadbalancingResource(projectID, "listener", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + display.OutputObject(listener, args[0], cloudLoadbalancerListenerTemplate, &flags.OutputFormatConfig) +} + +func CreateCloudLoadbalancerListener(cmd *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region := args[0] + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/listener", + projectID, url.PathEscape(region)) + + result, err := common.CreateResource( + cmd, + "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/listener", + endpoint, + LoadbalancerListenerCreationExample, + CloudLoadbalancerListenerCreateSpec, + assets.CloudOpenapiSchema, + nil, + ) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to create listener: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, result, "✅ Listener created successfully (ID: %s)", result["id"]) +} + +func EditCloudLoadbalancerListener(cmd *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "listener", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + if err := common.EditResource( + cmd, + "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/listener/{listenerId}", + fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/listener/%s", + projectID, url.PathEscape(region), url.PathEscape(args[0])), + CloudLoadbalancerListenerUpdateSpec, + assets.CloudOpenapiSchema, + ); err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } +} + +func DeleteCloudLoadbalancerListener(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "listener", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/listener/%s", + projectID, url.PathEscape(region), url.PathEscape(args[0])) + + if err := httpLib.Client.Delete(endpoint, nil); err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to delete listener: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ Listener %s deleted successfully", args[0]) +} diff --git a/internal/services/cloud/cloud_loadbalancer_log.go b/internal/services/cloud/cloud_loadbalancer_log.go new file mode 100644 index 00000000..8c65cee8 --- /dev/null +++ b/internal/services/cloud/cloud_loadbalancer_log.go @@ -0,0 +1,198 @@ +// SPDX-FileCopyrightText: 2025 OVH SAS +// +// SPDX-License-Identifier: Apache-2.0 + +package cloud + +import ( + _ "embed" + "fmt" + "net/url" + + "github.com/ovh/ovhcloud-cli/internal/assets" + "github.com/ovh/ovhcloud-cli/internal/display" + "github.com/ovh/ovhcloud-cli/internal/flags" + httpLib "github.com/ovh/ovhcloud-cli/internal/http" + "github.com/ovh/ovhcloud-cli/internal/services/common" + "github.com/spf13/cobra" +) + +var ( + logSubscriptionColumnsToDisplay = []string{"subscriptionId", "kind", "streamId", "createdAt"} + logKindColumnsToDisplay = []string{"name", "additionalReturnedFields", "displayName"} + + //go:embed templates/cloud_loadbalancer_log_subscription.tmpl + cloudLoadbalancerLogSubscriptionTemplate string + + //go:embed parameter-samples/loadbalancer-log-subscription-create.json + LoadbalancerLogSubscriptionCreationExample string + + CloudLoadbalancerLogSubscriptionCreateSpec struct { + Kind string `json:"kind,omitempty"` + StreamId string `json:"streamId,omitempty"` + } +) + +// Log Subscription operations + +func ListCloudLoadbalancerLogSubscriptions(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancer(projectID, args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s/log/subscription", + projectID, url.PathEscape(region), url.PathEscape(args[0])) + + common.ManageListRequestNoExpand(endpoint, logSubscriptionColumnsToDisplay, flags.GenericFilters) +} + +func GetCloudLoadbalancerLogSubscription(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancer(projectID, args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s/log/subscription/%s", + projectID, url.PathEscape(region), url.PathEscape(args[0]), url.PathEscape(args[1])) + + var subscription map[string]any + if err := httpLib.Client.Get(endpoint, &subscription); err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to fetch log subscription: %s", err) + return + } + + display.OutputObject(subscription, args[1], cloudLoadbalancerLogSubscriptionTemplate, &flags.OutputFormatConfig) +} + +func CreateCloudLoadbalancerLogSubscription(cmd *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancer(projectID, args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s/log/subscription", + projectID, url.PathEscape(region), url.PathEscape(args[0])) + + result, err := common.CreateResource( + cmd, + "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer/{loadBalancerId}/log/subscription", + endpoint, + LoadbalancerLogSubscriptionCreationExample, + CloudLoadbalancerLogSubscriptionCreateSpec, + assets.CloudOpenapiSchema, + nil, + ) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to create log subscription: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, result, "✅ Log subscription created successfully") +} + +func DeleteCloudLoadbalancerLogSubscription(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancer(projectID, args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s/log/subscription/%s", + projectID, url.PathEscape(region), url.PathEscape(args[0]), url.PathEscape(args[1])) + + if err := httpLib.Client.Delete(endpoint, nil); err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to delete log subscription: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ Log subscription %s deleted successfully", args[1]) +} + +// Log URL generation + +func GenerateCloudLoadbalancerLogURL(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancer(projectID, args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s/log/url", + projectID, url.PathEscape(region), url.PathEscape(args[0])) + + var result map[string]any + if err := httpLib.Client.Post(endpoint, nil, &result); err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to generate log URL: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, result, "✅ Temporary log URL generated successfully") +} + +// Log Kind operations + +func ListCloudLoadbalancerLogKinds(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/log/kind", + projectID, url.PathEscape(args[0])) + + common.ManageListRequestNoExpand(endpoint, logKindColumnsToDisplay, flags.GenericFilters) +} + +func GetCloudLoadbalancerLogKind(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/log/kind/%s", + projectID, url.PathEscape(args[0]), url.PathEscape(args[1])) + + var kind map[string]any + if err := httpLib.Client.Get(endpoint, &kind); err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to fetch log kind: %s", err) + return + } + + display.OutputObject(kind, args[1], "", &flags.OutputFormatConfig) +} diff --git a/internal/services/cloud/cloud_loadbalancer_pool.go b/internal/services/cloud/cloud_loadbalancer_pool.go new file mode 100644 index 00000000..ca1a4d9b --- /dev/null +++ b/internal/services/cloud/cloud_loadbalancer_pool.go @@ -0,0 +1,282 @@ +// SPDX-FileCopyrightText: 2025 OVH SAS +// +// SPDX-License-Identifier: Apache-2.0 + +package cloud + +import ( + _ "embed" + "fmt" + "net/url" + + "github.com/ovh/ovhcloud-cli/internal/assets" + "github.com/ovh/ovhcloud-cli/internal/display" + "github.com/ovh/ovhcloud-cli/internal/flags" + httpLib "github.com/ovh/ovhcloud-cli/internal/http" + "github.com/ovh/ovhcloud-cli/internal/services/common" + "github.com/spf13/cobra" +) + +var ( + poolColumnsToDisplay = []string{"id", "name", "algorithm", "protocol", "operatingStatus", "provisioningStatus"} + poolMemberColumnsToDisplay = []string{"id", "name", "address", "protocolPort", "weight", "operatingStatus"} + + //go:embed templates/cloud_loadbalancer_pool.tmpl + cloudLoadbalancerPoolTemplate string + + //go:embed templates/cloud_loadbalancer_pool_member.tmpl + cloudLoadbalancerPoolMemberTemplate string + + //go:embed parameter-samples/loadbalancer-pool-create.json + LoadbalancerPoolCreationExample string + + //go:embed parameter-samples/loadbalancer-pool-member-create.json + LoadbalancerPoolMemberCreationExample string + + CloudLoadbalancerPoolCreateSpec struct { + Algorithm string `json:"algorithm,omitempty"` + ListenerId string `json:"listenerId,omitempty"` + LoadbalancerId string `json:"loadbalancerId,omitempty"` + Name string `json:"name,omitempty"` + Protocol string `json:"protocol,omitempty"` + } + + CloudLoadbalancerPoolUpdateSpec struct { + Algorithm string `json:"algorithm,omitempty"` + Name string `json:"name,omitempty"` + } + + CloudLoadbalancerPoolMemberUpdateSpec struct { + Name string `json:"name,omitempty"` + Weight int `json:"weight,omitempty"` + } +) + +// Pool operations + +func ListCloudLoadbalancerPools(_ *cobra.Command, _ []string) { + listLoadbalancingResources("pool", poolColumnsToDisplay) +} + +func GetCloudLoadbalancerPool(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + _, pool, err := locateLoadbalancingResource(projectID, "pool", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + display.OutputObject(pool, args[0], cloudLoadbalancerPoolTemplate, &flags.OutputFormatConfig) +} + +func CreateCloudLoadbalancerPool(cmd *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region := args[0] + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/pool", + projectID, url.PathEscape(region)) + + result, err := common.CreateResource( + cmd, + "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/pool", + endpoint, + LoadbalancerPoolCreationExample, + CloudLoadbalancerPoolCreateSpec, + assets.CloudOpenapiSchema, + nil, + ) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to create pool: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, result, "✅ Pool created successfully (ID: %s)", result["id"]) +} + +func EditCloudLoadbalancerPool(cmd *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "pool", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + if err := common.EditResource( + cmd, + "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/pool/{poolId}", + fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/pool/%s", + projectID, url.PathEscape(region), url.PathEscape(args[0])), + CloudLoadbalancerPoolUpdateSpec, + assets.CloudOpenapiSchema, + ); err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } +} + +func DeleteCloudLoadbalancerPool(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "pool", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/pool/%s", + projectID, url.PathEscape(region), url.PathEscape(args[0])) + + if err := httpLib.Client.Delete(endpoint, nil); err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to delete pool: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ Pool %s deleted successfully", args[0]) +} + +// Pool Member operations + +func ListCloudLoadbalancerPoolMembers(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "pool", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/pool/%s/member", + projectID, url.PathEscape(region), url.PathEscape(args[0])) + + common.ManageListRequestNoExpand(endpoint, poolMemberColumnsToDisplay, flags.GenericFilters) +} + +func GetCloudLoadbalancerPoolMember(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "pool", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/pool/%s/member/%s", + projectID, url.PathEscape(region), url.PathEscape(args[0]), url.PathEscape(args[1])) + + var member map[string]any + if err := httpLib.Client.Get(endpoint, &member); err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to fetch pool member: %s", err) + return + } + + display.OutputObject(member, args[1], cloudLoadbalancerPoolMemberTemplate, &flags.OutputFormatConfig) +} + +func CreateCloudLoadbalancerPoolMember(cmd *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "pool", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/pool/%s/member", + projectID, url.PathEscape(region), url.PathEscape(args[0])) + + result, err := common.CreateResource( + cmd, + "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/pool/{poolId}/member", + endpoint, + LoadbalancerPoolMemberCreationExample, + struct{}{}, + assets.CloudOpenapiSchema, + nil, + ) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to create pool member: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, result, "✅ Pool member(s) created successfully") +} + +func EditCloudLoadbalancerPoolMember(cmd *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "pool", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + if err := common.EditResource( + cmd, + "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/pool/{poolId}/member/{memberId}", + fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/pool/%s/member/%s", + projectID, url.PathEscape(region), url.PathEscape(args[0]), url.PathEscape(args[1])), + CloudLoadbalancerPoolMemberUpdateSpec, + assets.CloudOpenapiSchema, + ); err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } +} + +func DeleteCloudLoadbalancerPoolMember(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + region, _, err := locateLoadbalancingResource(projectID, "pool", args[0]) + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/pool/%s/member/%s", + projectID, url.PathEscape(region), url.PathEscape(args[0]), url.PathEscape(args[1])) + + if err := httpLib.Client.Delete(endpoint, nil); err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to delete pool member: %s", err) + return + } + + display.OutputInfo(&flags.OutputFormatConfig, nil, "✅ Pool member %s deleted successfully", args[1]) +} diff --git a/internal/services/cloud/cloud_reference.go b/internal/services/cloud/cloud_reference.go index e1e5789a..ec6fe683 100644 --- a/internal/services/cloud/cloud_reference.go +++ b/internal/services/cloud/cloud_reference.go @@ -289,3 +289,22 @@ func ListLoadbalancerFlavors(cmd *cobra.Command, args []string) { endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/flavor", projectID, url.PathEscape(args[0])) common.ManageListRequestNoExpand(endpoint, []string{"id", "name", "region"}, flags.GenericFilters) } + +func GetLoadbalancerFlavor(_ *cobra.Command, args []string) { + projectID, err := getConfiguredCloudProject() + if err != nil { + display.OutputError(&flags.OutputFormatConfig, "%s", err) + return + } + + endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/flavor/%s", + projectID, url.PathEscape(args[0]), url.PathEscape(args[1])) + + var flavor map[string]any + if err := httpLib.Client.Get(endpoint, &flavor); err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to fetch loadbalancer flavor: %s", err) + return + } + + display.OutputObject(flavor, args[1], "", &flags.OutputFormatConfig) +} diff --git a/internal/services/cloud/parameter-samples/loadbalancer-associate-floating-ip.json b/internal/services/cloud/parameter-samples/loadbalancer-associate-floating-ip.json new file mode 100644 index 00000000..63ad14fc --- /dev/null +++ b/internal/services/cloud/parameter-samples/loadbalancer-associate-floating-ip.json @@ -0,0 +1,4 @@ +{ + "floatingIpId": "00000000-0000-0000-0000-000000000000", + "ip": "10.0.0.1" +} \ No newline at end of file diff --git a/internal/services/cloud/parameter-samples/loadbalancer-create-floating-ip.json b/internal/services/cloud/parameter-samples/loadbalancer-create-floating-ip.json new file mode 100644 index 00000000..f9d8e9a0 --- /dev/null +++ b/internal/services/cloud/parameter-samples/loadbalancer-create-floating-ip.json @@ -0,0 +1,3 @@ +{ + "ip": "10.0.0.1" +} \ No newline at end of file diff --git a/internal/services/cloud/parameter-samples/loadbalancer-create.json b/internal/services/cloud/parameter-samples/loadbalancer-create.json new file mode 100644 index 00000000..d3235a76 --- /dev/null +++ b/internal/services/cloud/parameter-samples/loadbalancer-create.json @@ -0,0 +1,12 @@ +{ + "flavorId": "00000000-0000-0000-0000-000000000000", + "name": "my-loadbalancer", + "network": { + "private": { + "network": { + "id": "00000000-0000-0000-0000-000000000000", + "subnetId": "00000000-0000-0000-0000-000000000000" + } + } + } +} \ No newline at end of file diff --git a/internal/services/cloud/parameter-samples/loadbalancer-health-monitor-create.json b/internal/services/cloud/parameter-samples/loadbalancer-health-monitor-create.json new file mode 100644 index 00000000..f1c946e5 --- /dev/null +++ b/internal/services/cloud/parameter-samples/loadbalancer-health-monitor-create.json @@ -0,0 +1,9 @@ +{ + "delay": 5, + "maxRetries": 3, + "maxRetriesDown": 3, + "monitorType": "http", + "name": "my-health-monitor", + "poolId": "00000000-0000-0000-0000-000000000000", + "timeout": 5 +} \ No newline at end of file diff --git a/internal/services/cloud/parameter-samples/loadbalancer-l7policy-create.json b/internal/services/cloud/parameter-samples/loadbalancer-l7policy-create.json new file mode 100644 index 00000000..c5a12aa4 --- /dev/null +++ b/internal/services/cloud/parameter-samples/loadbalancer-l7policy-create.json @@ -0,0 +1,7 @@ +{ + "action": "redirectToPool", + "listenerId": "00000000-0000-0000-0000-000000000000", + "name": "my-l7-policy", + "position": 1, + "redirectPoolId": "00000000-0000-0000-0000-000000000000" +} \ No newline at end of file diff --git a/internal/services/cloud/parameter-samples/loadbalancer-l7rule-create.json b/internal/services/cloud/parameter-samples/loadbalancer-l7rule-create.json new file mode 100644 index 00000000..0515d9fa --- /dev/null +++ b/internal/services/cloud/parameter-samples/loadbalancer-l7rule-create.json @@ -0,0 +1,5 @@ +{ + "compareType": "equalTo", + "ruleType": "path", + "value": "/api" +} \ No newline at end of file diff --git a/internal/services/cloud/parameter-samples/loadbalancer-listener-create.json b/internal/services/cloud/parameter-samples/loadbalancer-listener-create.json new file mode 100644 index 00000000..8311b0da --- /dev/null +++ b/internal/services/cloud/parameter-samples/loadbalancer-listener-create.json @@ -0,0 +1,6 @@ +{ + "loadbalancerId": "00000000-0000-0000-0000-000000000000", + "name": "my-listener", + "port": 80, + "protocol": "http" +} \ No newline at end of file diff --git a/internal/services/cloud/parameter-samples/loadbalancer-log-subscription-create.json b/internal/services/cloud/parameter-samples/loadbalancer-log-subscription-create.json new file mode 100644 index 00000000..4e3d1b46 --- /dev/null +++ b/internal/services/cloud/parameter-samples/loadbalancer-log-subscription-create.json @@ -0,0 +1,4 @@ +{ + "kind": "haproxy", + "streamId": "00000000-0000-0000-0000-000000000000" +} \ No newline at end of file diff --git a/internal/services/cloud/parameter-samples/loadbalancer-pool-create.json b/internal/services/cloud/parameter-samples/loadbalancer-pool-create.json new file mode 100644 index 00000000..fa53560f --- /dev/null +++ b/internal/services/cloud/parameter-samples/loadbalancer-pool-create.json @@ -0,0 +1,6 @@ +{ + "algorithm": "roundRobin", + "loadbalancerId": "00000000-0000-0000-0000-000000000000", + "name": "my-pool", + "protocol": "http" +} \ No newline at end of file diff --git a/internal/services/cloud/parameter-samples/loadbalancer-pool-member-create.json b/internal/services/cloud/parameter-samples/loadbalancer-pool-member-create.json new file mode 100644 index 00000000..9f5127ad --- /dev/null +++ b/internal/services/cloud/parameter-samples/loadbalancer-pool-member-create.json @@ -0,0 +1,10 @@ +{ + "members": [ + { + "address": "10.0.0.1", + "name": "my-member", + "protocolPort": 80, + "weight": 1 + } + ] +} \ No newline at end of file diff --git a/internal/services/cloud/templates/cloud_loadbalancer_health_monitor.tmpl b/internal/services/cloud/templates/cloud_loadbalancer_health_monitor.tmpl new file mode 100644 index 00000000..39f86430 --- /dev/null +++ b/internal/services/cloud/templates/cloud_loadbalancer_health_monitor.tmpl @@ -0,0 +1,17 @@ +💓 Health monitor {{.ServiceName}} +======= + +_{{index .Result "name"}}_ + +## General information + +**Monitor type**: {{index .Result "monitorType"}} +**Pool ID**: {{index .Result "poolId"}} +**Delay**: {{index .Result "delay"}}s +**Timeout**: {{index .Result "timeout"}}s +**Max retries**: {{index .Result "maxRetries"}} +**Max retries down**: {{index .Result "maxRetriesDown"}} +**Operating status**: {{index .Result "operatingStatus"}} +**Provisioning status**: {{index .Result "provisioningStatus"}} + +💡 Use option --json or --yaml to get the raw output with all information \ No newline at end of file diff --git a/internal/services/cloud/templates/cloud_loadbalancer_l7policy.tmpl b/internal/services/cloud/templates/cloud_loadbalancer_l7policy.tmpl new file mode 100644 index 00000000..4563a6d9 --- /dev/null +++ b/internal/services/cloud/templates/cloud_loadbalancer_l7policy.tmpl @@ -0,0 +1,18 @@ +📋 L7 policy {{.ServiceName}} +======= + +_{{index .Result "name"}}_ + +## General information + +**Action**: {{index .Result "action"}} +**Position**: {{index .Result "position"}} +**Listener ID**: {{index .Result "listenerId"}} +**Operating status**: {{index .Result "operatingStatus"}} +**Provisioning status**: {{index .Result "provisioningStatus"}} +**Description**: {{index .Result "description"}} +{{if index .Result "redirectPoolId"}}**Redirect pool ID**: {{index .Result "redirectPoolId"}}{{end}} +{{if index .Result "redirectUrl"}}**Redirect URL**: {{index .Result "redirectUrl"}}{{end}} +{{if index .Result "redirectPrefix"}}**Redirect prefix**: {{index .Result "redirectPrefix"}}{{end}} + +💡 Use option --json or --yaml to get the raw output with all information \ No newline at end of file diff --git a/internal/services/cloud/templates/cloud_loadbalancer_l7rule.tmpl b/internal/services/cloud/templates/cloud_loadbalancer_l7rule.tmpl new file mode 100644 index 00000000..e6b4e21f --- /dev/null +++ b/internal/services/cloud/templates/cloud_loadbalancer_l7rule.tmpl @@ -0,0 +1,14 @@ +📏 L7 rule {{.ServiceName}} +======= + +## General information + +**Rule type**: {{index .Result "ruleType"}} +**Compare type**: {{index .Result "compareType"}} +**Value**: {{index .Result "value"}} +**Key**: {{index .Result "key"}} +**Invert**: {{index .Result "invert"}} +**Operating status**: {{index .Result "operatingStatus"}} +**Provisioning status**: {{index .Result "provisioningStatus"}} + +💡 Use option --json or --yaml to get the raw output with all information \ No newline at end of file diff --git a/internal/services/cloud/templates/cloud_loadbalancer_listener.tmpl b/internal/services/cloud/templates/cloud_loadbalancer_listener.tmpl new file mode 100644 index 00000000..f4de4b7c --- /dev/null +++ b/internal/services/cloud/templates/cloud_loadbalancer_listener.tmpl @@ -0,0 +1,15 @@ +🔊 Listener {{.ServiceName}} +======= + +_{{index .Result "name"}}_ + +## General information + +**Protocol**: {{index .Result "protocol"}} +**Port**: {{index .Result "port"}} +**Operating status**: {{index .Result "operatingStatus"}} +**Provisioning status**: {{index .Result "provisioningStatus"}} +**Default pool ID**: {{index .Result "defaultPoolId"}} +**Description**: {{index .Result "description"}} + +💡 Use option --json or --yaml to get the raw output with all information \ No newline at end of file diff --git a/internal/services/cloud/templates/cloud_loadbalancer_log_subscription.tmpl b/internal/services/cloud/templates/cloud_loadbalancer_log_subscription.tmpl new file mode 100644 index 00000000..780eecee --- /dev/null +++ b/internal/services/cloud/templates/cloud_loadbalancer_log_subscription.tmpl @@ -0,0 +1,13 @@ +📝 Log subscription {{.ServiceName}} +======= + +## General information + +**Subscription ID**: {{index .Result "subscriptionId"}} +**Kind**: {{index .Result "kind"}} +**Stream ID**: {{index .Result "streamId"}} +**Service name**: {{index .Result "serviceName"}} +**Created at**: {{index .Result "createdAt"}} +**Updated at**: {{index .Result "updatedAt"}} + +💡 Use option --json or --yaml to get the raw output with all information \ No newline at end of file diff --git a/internal/services/cloud/templates/cloud_loadbalancer_pool.tmpl b/internal/services/cloud/templates/cloud_loadbalancer_pool.tmpl new file mode 100644 index 00000000..3e24c64b --- /dev/null +++ b/internal/services/cloud/templates/cloud_loadbalancer_pool.tmpl @@ -0,0 +1,15 @@ +🏊 Pool {{.ServiceName}} +======= + +_{{index .Result "name"}}_ + +## General information + +**Algorithm**: {{index .Result "algorithm"}} +**Protocol**: {{index .Result "protocol"}} +**Operating status**: {{index .Result "operatingStatus"}} +**Provisioning status**: {{index .Result "provisioningStatus"}} +**Loadbalancer ID**: {{index .Result "loadbalancerId"}} +**Listener ID**: {{index .Result "listenerId"}} + +💡 Use option --json or --yaml to get the raw output with all information \ No newline at end of file diff --git a/internal/services/cloud/templates/cloud_loadbalancer_pool_member.tmpl b/internal/services/cloud/templates/cloud_loadbalancer_pool_member.tmpl new file mode 100644 index 00000000..8b116148 --- /dev/null +++ b/internal/services/cloud/templates/cloud_loadbalancer_pool_member.tmpl @@ -0,0 +1,14 @@ +👤 Pool member {{.ServiceName}} +======= + +_{{index .Result "name"}}_ + +## General information + +**Address**: {{index .Result "address"}} +**Protocol port**: {{index .Result "protocolPort"}} +**Weight**: {{index .Result "weight"}} +**Operating status**: {{index .Result "operatingStatus"}} +**Provisioning status**: {{index .Result "provisioningStatus"}} + +💡 Use option --json or --yaml to get the raw output with all information \ No newline at end of file diff --git a/internal/services/cloud/templates/cloud_loadbalancer_stats.tmpl b/internal/services/cloud/templates/cloud_loadbalancer_stats.tmpl new file mode 100644 index 00000000..27ebbad7 --- /dev/null +++ b/internal/services/cloud/templates/cloud_loadbalancer_stats.tmpl @@ -0,0 +1,10 @@ +📊 Load balancer statistics {{.ServiceName}} +======= + +**Active connections**: {{index .Result "activeConnections"}} +**Bytes in**: {{index .Result "bytesIn"}} +**Bytes out**: {{index .Result "bytesOut"}} +**Request errors**: {{index .Result "requestErrors"}} +**Total connections**: {{index .Result "totalConnections"}} + +💡 Use option --json or --yaml to get the raw output with all information \ No newline at end of file From 5a1ee2200478c530921253e219f923155930591a Mon Sep 17 00:00:00 2001 From: Arthur Amstutz Date: Wed, 11 Feb 2026 21:37:19 +0100 Subject: [PATCH 2/3] chore(loadbalancer): Add tests Signed-off-by: Arthur Amstutz --- internal/cmd/cloud_network_loadbalancer.go | 11 +- .../cmd/cloud_network_loadbalancer_test.go | 969 ++++++++++++++++++ internal/services/cloud/cloud_loadbalancer.go | 18 +- .../cloud_loadbalancer_health_monitor.go | 2 +- .../cloud/cloud_loadbalancer_l7policy.go | 2 +- .../cloud/cloud_loadbalancer_listener.go | 2 +- .../services/cloud/cloud_loadbalancer_log.go | 19 +- 7 files changed, 1011 insertions(+), 12 deletions(-) create mode 100644 internal/cmd/cloud_network_loadbalancer_test.go diff --git a/internal/cmd/cloud_network_loadbalancer.go b/internal/cmd/cloud_network_loadbalancer.go index fdeb2059..c7b7194c 100644 --- a/internal/cmd/cloud_network_loadbalancer.go +++ b/internal/cmd/cloud_network_loadbalancer.go @@ -87,6 +87,10 @@ There are three ways to define the parameters: cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.Name, "name", "", "Name of the loadbalancer") cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.FlavorId, "flavor", "", "Flavor ID (can be retrieved with 'cloud reference loadbalancer list-flavors ')") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.Network.Private.Network.Id, "network-id", "", "Network ID") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.Network.Private.Network.SubnetId, "subnet-id", "", "Subnet ID") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.Network.Private.FloatingIp.Id, "floating-ip", "", "Floating IP ID to associate to the loadbalancer") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.Network.Private.Gateway.Id, "gateway", "", "Gateway ID to associate to the loadbalancer") addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer", "post", cloud.LoadbalancerCreationExample, nil) addInteractiveEditorFlag(cmd) @@ -557,12 +561,15 @@ func initLogSubCommands(loadbalancerCmd *cobra.Command) { Args: cobra.ExactArgs(2), }) - logCmd.AddCommand(&cobra.Command{ + logURLCmd := &cobra.Command{ Use: "generate-url ", Short: "Generate a temporary URL to retrieve logs", Run: cloud.GenerateCloudLoadbalancerLogURL, Args: cobra.ExactArgs(1), - }) + } + logURLCmd.Flags().StringVar(&cloud.CloudLoadbalancerLogURLSpec.Kind, "kind", "", "Log kind (e.g., haproxy)") + logURLCmd.MarkFlagRequired("kind") + logCmd.AddCommand(logURLCmd) // Log Subscription sub-commands subscriptionCmd := &cobra.Command{ diff --git a/internal/cmd/cloud_network_loadbalancer_test.go b/internal/cmd/cloud_network_loadbalancer_test.go new file mode 100644 index 00000000..02d5ae03 --- /dev/null +++ b/internal/cmd/cloud_network_loadbalancer_test.go @@ -0,0 +1,969 @@ +// SPDX-FileCopyrightText: 2025 OVH SAS +// +// SPDX-License-Identifier: Apache-2.0 + +package cmd_test + +import ( + "encoding/json" + "net/http" + + "github.com/jarcoal/httpmock" + "github.com/maxatome/go-testdeep/td" + "github.com/ovh/ovhcloud-cli/internal/cmd" +) + +// --------------------------------------------------------------------------- +// Helpers: region mocks shared across loadbalancing tests +// --------------------------------------------------------------------------- + +// registerLoadbalancingRegionMocks sets up the standard region discovery responses +// used by locateLoadbalancer, locateLoadbalancingResource, and listLoadbalancingResources. +// GRA11 and SBG5 have the octavialoadbalancer feature; BHS5 does not. +func registerLoadbalancingRegionMocks() { + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region", + httpmock.NewStringResponder(200, `["GRA11", "SBG5", "BHS5"]`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11", + httpmock.NewStringResponder(200, `{ + "name": "GRA11", + "type": "region", + "status": "UP", + "services": [{"name": "octavialoadbalancer", "status": "UP"}] + }`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5", + httpmock.NewStringResponder(200, `{ + "name": "SBG5", + "type": "region", + "status": "UP", + "services": [{"name": "octavialoadbalancer", "status": "UP"}] + }`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/BHS5", + httpmock.NewStringResponder(200, `{ + "name": "BHS5", + "type": "region", + "status": "UP", + "services": [] + }`)) +} + +// --------------------------------------------------------------------------- +// Loadbalancer – list +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerListCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/loadbalancer", + httpmock.NewStringResponder(200, `[ + { + "id": "lb-gra-001", + "name": "my-lb-gra", + "region": "GRA11", + "provisioningStatus": "active", + "operatingStatus": "online" + } + ]`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/loadbalancer", + httpmock.NewStringResponder(200, `[ + { + "id": "lb-sbg-001", + "name": "my-lb-sbg", + "region": "SBG5", + "provisioningStatus": "active", + "operatingStatus": "online" + } + ]`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "ls", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`[ + { + "id": "lb-gra-001", + "name": "my-lb-gra", + "region": "GRA11", + "provisioningStatus": "active", + "operatingStatus": "online" + }, + { + "id": "lb-sbg-001", + "name": "my-lb-sbg", + "region": "SBG5", + "provisioningStatus": "active", + "operatingStatus": "online" + } + ]`)) +} + +// --------------------------------------------------------------------------- +// Loadbalancer – delete +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerDeleteCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + // locateLoadbalancer: 404 on GRA11, found on SBG5 + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/loadbalancer/lb-sbg-001", + httpmock.NewStringResponder(404, `{"message":"not found"}`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/loadbalancer/lb-sbg-001", + httpmock.NewStringResponder(200, `{"id":"lb-sbg-001","name":"my-lb-sbg","region":"SBG5"}`)) + + httpmock.RegisterResponder(http.MethodDelete, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/loadbalancer/lb-sbg-001", + httpmock.NewStringResponder(200, `null`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "delete", "lb-sbg-001", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`{ + "message": "✅ Loadbalancer lb-sbg-001 deleted successfully" + }`)) +} + +// --------------------------------------------------------------------------- +// Loadbalancer – stats +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerStatsCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/loadbalancer/lb-gra-001", + httpmock.NewStringResponder(200, `{"id":"lb-gra-001","name":"my-lb-gra","region":"GRA11"}`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/loadbalancer/lb-gra-001/stats", + httpmock.NewStringResponder(200, `{ + "activeConnections": 42, + "bytesIn": 1024000, + "bytesOut": 2048000, + "requestErrors": 3, + "totalConnections": 500 + }`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "stats", "lb-gra-001", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`{ + "activeConnections": 42, + "bytesIn": 1024000, + "bytesOut": 2048000, + "requestErrors": 3, + "totalConnections": 500 + }`)) +} + +// --------------------------------------------------------------------------- +// Listener – list +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerListenerListCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/listener", + httpmock.NewStringResponder(200, `[ + { + "id": "lis-001", + "name": "my-listener", + "protocol": "http", + "port": 80, + "operatingStatus": "online", + "provisioningStatus": "active" + } + ]`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/listener", + httpmock.NewStringResponder(200, `[]`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "listener", "ls", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`[ + { + "id": "lis-001", + "name": "my-listener", + "protocol": "http", + "port": 80, + "operatingStatus": "online", + "provisioningStatus": "active" + } + ]`)) +} + +// --------------------------------------------------------------------------- +// Listener – get +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerListenerGetCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/listener/lis-001", + httpmock.NewStringResponder(404, `{"message":"not found"}`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/listener/lis-001", + httpmock.NewStringResponder(200, `{ + "id": "lis-001", + "name": "my-listener", + "protocol": "http", + "port": 80, + "operatingStatus": "online", + "provisioningStatus": "active", + "defaultPoolId": "pool-001", + "description": "My HTTP listener" + }`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "listener", "get", "lis-001", "--cloud-project", "fakeProjectID") + require.CmpNoError(err) + assert.Cmp(cleanWhitespacesHelper(out), ` + # 🔊 Listener lis-001 + + *my-listener* + + ## General information + + **Protocol**: http + **Port**: 80 + **Operating status**: online + **Provisioning status**: active + **Default pool ID**: pool-001 + **Description**: My HTTP listener + + 💡 Use option --json or --yaml to get the raw output with all information + +`) +} + +// --------------------------------------------------------------------------- +// Listener – delete +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerListenerDeleteCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/listener/lis-001", + httpmock.NewStringResponder(200, `{"id":"lis-001"}`)) + + httpmock.RegisterResponder(http.MethodDelete, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/listener/lis-001", + httpmock.NewStringResponder(200, `null`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "listener", "delete", "lis-001", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`{ + "message": "✅ Listener lis-001 deleted successfully" + }`)) +} + +// --------------------------------------------------------------------------- +// Pool – list +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerPoolListCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/pool", + httpmock.NewStringResponder(200, `[ + { + "id": "pool-001", + "name": "my-pool", + "algorithm": "roundRobin", + "protocol": "http", + "operatingStatus": "online", + "provisioningStatus": "active" + } + ]`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/pool", + httpmock.NewStringResponder(200, `[]`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "pool", "ls", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`[ + { + "id": "pool-001", + "name": "my-pool", + "algorithm": "roundRobin", + "protocol": "http", + "operatingStatus": "online", + "provisioningStatus": "active" + } + ]`)) +} + +// --------------------------------------------------------------------------- +// Pool – get +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerPoolGetCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/pool/pool-001", + httpmock.NewStringResponder(200, `{ + "id": "pool-001", + "name": "my-pool", + "algorithm": "roundRobin", + "protocol": "http", + "operatingStatus": "online", + "provisioningStatus": "active", + "loadbalancerId": "lb-gra-001", + "listenerId": "lis-001" + }`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "pool", "get", "pool-001", "--cloud-project", "fakeProjectID") + require.CmpNoError(err) + assert.Cmp(cleanWhitespacesHelper(out), ` + # 🏊 Pool pool-001 + + *my-pool* + + ## General information + + **Algorithm**: roundRobin + **Protocol**: http + **Operating status**: online + **Provisioning status**: active + **Loadbalancer ID**: lb-gra-001 + **Listener ID**: lis-001 + + 💡 Use option --json or --yaml to get the raw output with all information + +`) +} + +// --------------------------------------------------------------------------- +// Pool – delete +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerPoolDeleteCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/pool/pool-001", + httpmock.NewStringResponder(200, `{"id":"pool-001"}`)) + + httpmock.RegisterResponder(http.MethodDelete, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/pool/pool-001", + httpmock.NewStringResponder(200, `null`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "pool", "delete", "pool-001", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`{ + "message": "✅ Pool pool-001 deleted successfully" + }`)) +} + +// --------------------------------------------------------------------------- +// Pool Member – list +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerPoolMemberListCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + // Locate the pool first + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/pool/pool-001", + httpmock.NewStringResponder(200, `{"id":"pool-001"}`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/pool/pool-001/member", + httpmock.NewStringResponder(200, `[ + { + "id": "mem-001", + "name": "backend-1", + "address": "10.0.0.10", + "protocolPort": 8080, + "weight": 1, + "operatingStatus": "online" + }, + { + "id": "mem-002", + "name": "backend-2", + "address": "10.0.0.11", + "protocolPort": 8080, + "weight": 2, + "operatingStatus": "online" + } + ]`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "pool", "member", "ls", "pool-001", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`[ + { + "id": "mem-001", + "name": "backend-1", + "address": "10.0.0.10", + "protocolPort": 8080, + "weight": 1, + "operatingStatus": "online" + }, + { + "id": "mem-002", + "name": "backend-2", + "address": "10.0.0.11", + "protocolPort": 8080, + "weight": 2, + "operatingStatus": "online" + } + ]`)) +} + +// --------------------------------------------------------------------------- +// Pool Member – get +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerPoolMemberGetCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/pool/pool-001", + httpmock.NewStringResponder(200, `{"id":"pool-001"}`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/pool/pool-001/member/mem-001", + httpmock.NewStringResponder(200, `{ + "id": "mem-001", + "name": "backend-1", + "address": "10.0.0.10", + "protocolPort": 8080, + "weight": 1, + "operatingStatus": "online", + "provisioningStatus": "active" + }`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "pool", "member", "get", "pool-001", "mem-001", "--cloud-project", "fakeProjectID") + require.CmpNoError(err) + assert.Cmp(cleanWhitespacesHelper(out), ` + # 👤 Pool member mem-001 + + *backend-1* + + ## General information + + **Address**: 10.0.0.10 + **Protocol port**: 8080 + **Weight**: 1 + **Operating status**: online + **Provisioning status**: active + + 💡 Use option --json or --yaml to get the raw output with all information + +`) +} + +// --------------------------------------------------------------------------- +// Pool Member – delete +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerPoolMemberDeleteCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/pool/pool-001", + httpmock.NewStringResponder(200, `{"id":"pool-001"}`)) + + httpmock.RegisterResponder(http.MethodDelete, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/pool/pool-001/member/mem-001", + httpmock.NewStringResponder(200, `null`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "pool", "member", "delete", "pool-001", "mem-001", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`{ + "message": "✅ Pool member mem-001 deleted successfully" + }`)) +} + +// --------------------------------------------------------------------------- +// Health Monitor – list +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerHealthMonitorListCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/healthMonitor", + httpmock.NewStringResponder(200, `[ + { + "id": "hm-001", + "name": "my-health-monitor", + "monitorType": "http", + "poolId": "pool-001", + "operatingStatus": "online", + "provisioningStatus": "active" + } + ]`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/healthMonitor", + httpmock.NewStringResponder(200, `[]`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "health-monitor", "ls", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`[ + { + "id": "hm-001", + "name": "my-health-monitor", + "monitorType": "http", + "poolId": "pool-001", + "operatingStatus": "online", + "provisioningStatus": "active" + } + ]`)) +} + +// --------------------------------------------------------------------------- +// Health Monitor – get +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerHealthMonitorGetCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/healthMonitor/hm-001", + httpmock.NewStringResponder(200, `{ + "id": "hm-001", + "name": "my-health-monitor", + "monitorType": "http", + "poolId": "pool-001", + "delay": 5, + "timeout": 10, + "maxRetries": 3, + "maxRetriesDown": 3, + "operatingStatus": "online", + "provisioningStatus": "active" + }`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "health-monitor", "get", "hm-001", "--cloud-project", "fakeProjectID") + require.CmpNoError(err) + assert.Cmp(cleanWhitespacesHelper(out), ` + # 💓 Health monitor hm-001 + + *my-health-monitor* + + ## General information + + **Monitor type**: http + **Pool ID**: pool-001 + **Delay**: 5s + **Timeout**: 10s + **Max retries**: 3 + **Max retries down**: 3 + **Operating status**: online + **Provisioning status**: active + + 💡 Use option --json or --yaml to get the raw output with all information + +`) +} + +// --------------------------------------------------------------------------- +// Health Monitor – delete +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerHealthMonitorDeleteCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/healthMonitor/hm-001", + httpmock.NewStringResponder(200, `{"id":"hm-001"}`)) + + httpmock.RegisterResponder(http.MethodDelete, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/healthMonitor/hm-001", + httpmock.NewStringResponder(200, `null`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "health-monitor", "delete", "hm-001", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`{ + "message": "✅ Health monitor hm-001 deleted successfully" + }`)) +} + +// --------------------------------------------------------------------------- +// L7 Policy – list +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerL7PolicyListCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/l7Policy", + httpmock.NewStringResponder(200, `[ + { + "id": "l7p-001", + "name": "my-policy", + "action": "redirectToPool", + "listenerId": "lis-001", + "position": 1, + "operatingStatus": "online" + } + ]`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/l7Policy", + httpmock.NewStringResponder(200, `[]`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "l7policy", "ls", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`[ + { + "id": "l7p-001", + "name": "my-policy", + "action": "redirectToPool", + "listenerId": "lis-001", + "position": 1, + "operatingStatus": "online" + } + ]`)) +} + +// --------------------------------------------------------------------------- +// L7 Policy – get +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerL7PolicyGetCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/l7Policy/l7p-001", + httpmock.NewStringResponder(200, `{ + "id": "l7p-001", + "name": "my-policy", + "action": "redirectToPool", + "listenerId": "lis-001", + "position": 1, + "operatingStatus": "online", + "provisioningStatus": "active", + "description": "Redirect to backend pool", + "redirectPoolId": "pool-001", + "redirectUrl": "", + "redirectPrefix": "" + }`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "l7policy", "get", "l7p-001", "--cloud-project", "fakeProjectID") + require.CmpNoError(err) + assert.Cmp(cleanWhitespacesHelper(out), ` + # 📋 L7 policy l7p-001 + + *my-policy* + + ## General information + + **Action**: redirectToPool + **Position**: 1 + **Listener ID**: lis-001 + **Operating status**: online + **Provisioning status**: active + **Description**: Redirect to backend pool + **Redirect pool ID**: pool-001 + + 💡 Use option --json or --yaml to get the raw output with all information + +`) +} + +// --------------------------------------------------------------------------- +// L7 Policy – delete +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerL7PolicyDeleteCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/l7Policy/l7p-001", + httpmock.NewStringResponder(200, `{"id":"l7p-001"}`)) + + httpmock.RegisterResponder(http.MethodDelete, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/l7Policy/l7p-001", + httpmock.NewStringResponder(200, `null`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "l7policy", "delete", "l7p-001", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`{ + "message": "✅ L7 policy l7p-001 deleted successfully" + }`)) +} + +// --------------------------------------------------------------------------- +// L7 Rule – list +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerL7RuleListCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + // Locate the L7 policy first + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/l7Policy/l7p-001", + httpmock.NewStringResponder(200, `{"id":"l7p-001"}`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/l7Policy/l7p-001/l7Rule", + httpmock.NewStringResponder(200, `[ + { + "id": "l7r-001", + "ruleType": "header", + "compareType": "equalTo", + "value": "application/json", + "key": "Content-Type", + "invert": false + } + ]`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "l7policy", "l7rule", "ls", "l7p-001", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`[ + { + "id": "l7r-001", + "ruleType": "header", + "compareType": "equalTo", + "value": "application/json", + "key": "Content-Type", + "invert": false + } + ]`)) +} + +// --------------------------------------------------------------------------- +// L7 Rule – get +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerL7RuleGetCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/l7Policy/l7p-001", + httpmock.NewStringResponder(200, `{"id":"l7p-001"}`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/l7Policy/l7p-001/l7Rule/l7r-001", + httpmock.NewStringResponder(200, `{ + "id": "l7r-001", + "ruleType": "header", + "compareType": "equalTo", + "value": "application/json", + "key": "Content-Type", + "invert": false, + "operatingStatus": "online", + "provisioningStatus": "active" + }`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "l7policy", "l7rule", "get", "l7p-001", "l7r-001", "--cloud-project", "fakeProjectID") + require.CmpNoError(err) + assert.Cmp(cleanWhitespacesHelper(out), ` + # 📏 L7 rule l7r-001 + + ## General information + + **Rule type**: header + **Compare type**: equalTo + **Value**: application/json + **Key**: Content-Type + **Invert**: false + **Operating status**: online + **Provisioning status**: active + + 💡 Use option --json or --yaml to get the raw output with all information + +`) +} + +// --------------------------------------------------------------------------- +// L7 Rule – delete +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerL7RuleDeleteCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/l7Policy/l7p-001", + httpmock.NewStringResponder(200, `{"id":"l7p-001"}`)) + + httpmock.RegisterResponder(http.MethodDelete, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/l7Policy/l7p-001/l7Rule/l7r-001", + httpmock.NewStringResponder(200, `null`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "l7policy", "l7rule", "delete", "l7p-001", "l7r-001", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`{ + "message": "✅ L7 rule l7r-001 deleted successfully" + }`)) +} + +// --------------------------------------------------------------------------- +// Log – list kinds +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerLogKindListCmd(assert, require *td.T) { + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/log/kind", + httpmock.NewStringResponder(200, `["haproxy"]`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "log", "list-kinds", "GRA11", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`{ + "kinds": [ + "haproxy" + ] + }`)) +} + +// --------------------------------------------------------------------------- +// Log – get kind +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerLogKindGetCmd(assert, require *td.T) { + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/log/kind/haproxy", + httpmock.NewStringResponder(200, `{ + "name": "haproxy", + "additionalReturnedFields": ["remote_addr", "request_path"], + "displayName": "HAProxy logs" + }`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "log", "get-kind", "GRA11", "haproxy", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`{ + "name": "haproxy", + "additionalReturnedFields": ["remote_addr", "request_path"], + "displayName": "HAProxy logs" + }`)) +} + +// --------------------------------------------------------------------------- +// Log – generate URL +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerLogGenerateURLCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/loadbalancer/lb-gra-001", + httpmock.NewStringResponder(200, `{"id":"lb-gra-001","name":"my-lb","region":"GRA11"}`)) + + httpmock.RegisterResponder(http.MethodPost, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/loadbalancer/lb-gra-001/log/url", + httpmock.NewStringResponder(200, `{ + "url": "https://logs.example.com/temp/abc123" + }`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "log", "generate-url", "lb-gra-001", "--kind", "haproxy", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`{ + "message": "✅ Temporary log URL generated successfully: https://logs.example.com/temp/abc123", + "details": { + "url": "https://logs.example.com/temp/abc123" + } + }`)) +} + +// --------------------------------------------------------------------------- +// Log Subscription – list +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerLogSubscriptionListCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/loadbalancer/lb-gra-001", + httpmock.NewStringResponder(200, `{"id":"lb-gra-001","name":"my-lb","region":"GRA11"}`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/loadbalancer/lb-gra-001/log/subscription", + httpmock.NewStringResponder(200, `[ + { + "subscriptionId": "sub-001", + "kind": "haproxy", + "streamId": "stream-abc", + "createdAt": "2024-01-15T10:30:00Z" + } + ]`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "log", "subscription", "ls", "lb-gra-001", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`[ + { + "subscriptionId": "sub-001", + "kind": "haproxy", + "streamId": "stream-abc", + "createdAt": "2024-01-15T10:30:00Z" + } + ]`)) +} + +// --------------------------------------------------------------------------- +// Log Subscription – get +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerLogSubscriptionGetCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/loadbalancer/lb-gra-001", + httpmock.NewStringResponder(200, `{"id":"lb-gra-001","name":"my-lb","region":"GRA11"}`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/loadbalancer/lb-gra-001/log/subscription/sub-001", + httpmock.NewStringResponder(200, `{ + "subscriptionId": "sub-001", + "kind": "haproxy", + "streamId": "stream-abc", + "serviceName": "ldp-service", + "createdAt": "2024-01-15T10:30:00Z", + "updatedAt": "2024-01-15T10:30:00Z" + }`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "log", "subscription", "get", "lb-gra-001", "sub-001", "--cloud-project", "fakeProjectID") + require.CmpNoError(err) + assert.Cmp(cleanWhitespacesHelper(out), ` + # 📝 Log subscription sub-001 + + ## General information + + **Subscription ID**: sub-001 + **Kind**: haproxy + **Stream ID**: stream-abc + **Service name**: ldp-service + **Created at**: 2024-01-15T10:30:00Z + **Updated at**: 2024-01-15T10:30:00Z + + 💡 Use option --json or --yaml to get the raw output with all information + +`) +} + +// --------------------------------------------------------------------------- +// Log Subscription – delete +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerLogSubscriptionDeleteCmd(assert, require *td.T) { + registerLoadbalancingRegionMocks() + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/loadbalancer/lb-gra-001", + httpmock.NewStringResponder(200, `{"id":"lb-gra-001","name":"my-lb","region":"GRA11"}`)) + + httpmock.RegisterResponder(http.MethodDelete, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/loadbalancer/lb-gra-001/log/subscription/sub-001", + httpmock.NewStringResponder(200, `null`)) + + out, err := cmd.Execute("cloud", "network", "loadbalancer", "log", "subscription", "delete", "lb-gra-001", "sub-001", "--cloud-project", "fakeProjectID", "--json") + require.CmpNoError(err) + assert.Cmp(json.RawMessage(out), td.JSON(`{ + "message": "✅ Log subscription sub-001 deleted successfully" + }`)) +} diff --git a/internal/services/cloud/cloud_loadbalancer.go b/internal/services/cloud/cloud_loadbalancer.go index 4cbbe906..bba058c9 100644 --- a/internal/services/cloud/cloud_loadbalancer.go +++ b/internal/services/cloud/cloud_loadbalancer.go @@ -45,6 +45,20 @@ var ( CloudLoadbalancerCreateSpec struct { FlavorId string `json:"flavorId,omitempty"` Name string `json:"name,omitempty"` + Network struct { + Private struct { + FloatingIp struct { + Id string `json:"id,omitempty"` + } `json:"floatingIp,omitzero"` + Gateway struct { + Id string `json:"id,omitempty"` + } `json:"gateway,omitzero"` + Network struct { + Id string `json:"id,omitempty"` + SubnetId string `json:"subnetId,omitempty"` + } `json:"network,omitzero"` + } `json:"private,omitzero"` + } `json:"network,omitzero"` } CloudLoadbalancerAssociateFloatingIpSpec struct { @@ -209,7 +223,7 @@ func CreateCloudLoadbalancer(cmd *cobra.Command, args []string) { LoadbalancerCreationExample, CloudLoadbalancerCreateSpec, assets.CloudOpenapiSchema, - nil, + []string{"name", "flavorId", "network"}, ) if err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to create loadbalancer: %s", err) @@ -324,7 +338,7 @@ func CreateFloatingIpForLoadbalancer(cmd *cobra.Command, args []string) { LoadbalancerCreateFloatingIpExample, CloudLoadbalancerCreateFloatingIpSpec, assets.CloudOpenapiSchema, - nil, + []string{"ip"}, ) if err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to create floating IP: %s", err) diff --git a/internal/services/cloud/cloud_loadbalancer_health_monitor.go b/internal/services/cloud/cloud_loadbalancer_health_monitor.go index baedf5c5..e44a9971 100644 --- a/internal/services/cloud/cloud_loadbalancer_health_monitor.go +++ b/internal/services/cloud/cloud_loadbalancer_health_monitor.go @@ -83,7 +83,7 @@ func CreateCloudLoadbalancerHealthMonitor(cmd *cobra.Command, args []string) { LoadbalancerHealthMonitorCreationExample, CloudLoadbalancerHealthMonitorCreateSpec, assets.CloudOpenapiSchema, - nil, + []string{"delay", "maxRetries", "monitorType", "name", "poolId", "timeout"}, ) if err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to create health monitor: %s", err) diff --git a/internal/services/cloud/cloud_loadbalancer_l7policy.go b/internal/services/cloud/cloud_loadbalancer_l7policy.go index f424803f..106b0ce0 100644 --- a/internal/services/cloud/cloud_loadbalancer_l7policy.go +++ b/internal/services/cloud/cloud_loadbalancer_l7policy.go @@ -114,7 +114,7 @@ func CreateCloudLoadbalancerL7Policy(cmd *cobra.Command, args []string) { LoadbalancerL7PolicyCreationExample, CloudLoadbalancerL7PolicyCreateSpec, assets.CloudOpenapiSchema, - nil, + []string{"action", "listenerId"}, ) if err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to create L7 policy: %s", err) diff --git a/internal/services/cloud/cloud_loadbalancer_listener.go b/internal/services/cloud/cloud_loadbalancer_listener.go index a39de8dd..36417150 100644 --- a/internal/services/cloud/cloud_loadbalancer_listener.go +++ b/internal/services/cloud/cloud_loadbalancer_listener.go @@ -80,7 +80,7 @@ func CreateCloudLoadbalancerListener(cmd *cobra.Command, args []string) { LoadbalancerListenerCreationExample, CloudLoadbalancerListenerCreateSpec, assets.CloudOpenapiSchema, - nil, + []string{"loadbalancerId", "name", "port", "protocol"}, ) if err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to create listener: %s", err) diff --git a/internal/services/cloud/cloud_loadbalancer_log.go b/internal/services/cloud/cloud_loadbalancer_log.go index 8c65cee8..24481faf 100644 --- a/internal/services/cloud/cloud_loadbalancer_log.go +++ b/internal/services/cloud/cloud_loadbalancer_log.go @@ -19,7 +19,6 @@ import ( var ( logSubscriptionColumnsToDisplay = []string{"subscriptionId", "kind", "streamId", "createdAt"} - logKindColumnsToDisplay = []string{"name", "additionalReturnedFields", "displayName"} //go:embed templates/cloud_loadbalancer_log_subscription.tmpl cloudLoadbalancerLogSubscriptionTemplate string @@ -31,6 +30,10 @@ var ( Kind string `json:"kind,omitempty"` StreamId string `json:"streamId,omitempty"` } + + CloudLoadbalancerLogURLSpec struct { + Kind string `json:"kind"` + } ) // Log Subscription operations @@ -102,7 +105,7 @@ func CreateCloudLoadbalancerLogSubscription(cmd *cobra.Command, args []string) { LoadbalancerLogSubscriptionCreationExample, CloudLoadbalancerLogSubscriptionCreateSpec, assets.CloudOpenapiSchema, - nil, + []string{"kind", "streamId"}, ) if err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to create log subscription: %s", err) @@ -155,12 +158,12 @@ func GenerateCloudLoadbalancerLogURL(_ *cobra.Command, args []string) { projectID, url.PathEscape(region), url.PathEscape(args[0])) var result map[string]any - if err := httpLib.Client.Post(endpoint, nil, &result); err != nil { + if err := httpLib.Client.Post(endpoint, CloudLoadbalancerLogURLSpec, &result); err != nil { display.OutputError(&flags.OutputFormatConfig, "failed to generate log URL: %s", err) return } - display.OutputInfo(&flags.OutputFormatConfig, result, "✅ Temporary log URL generated successfully") + display.OutputInfo(&flags.OutputFormatConfig, result, "✅ Temporary log URL generated successfully: %s", result["url"]) } // Log Kind operations @@ -175,7 +178,13 @@ func ListCloudLoadbalancerLogKinds(_ *cobra.Command, args []string) { endpoint := fmt.Sprintf("/v1/cloud/project/%s/region/%s/loadbalancing/log/kind", projectID, url.PathEscape(args[0])) - common.ManageListRequestNoExpand(endpoint, logKindColumnsToDisplay, flags.GenericFilters) + var kinds []string + if err := httpLib.Client.Get(endpoint, &kinds); err != nil { + display.OutputError(&flags.OutputFormatConfig, "failed to fetch log kinds: %s", err) + return + } + + display.OutputObject(map[string]any{"kinds": kinds}, "Log kinds", "", &flags.OutputFormatConfig) } func GetCloudLoadbalancerLogKind(_ *cobra.Command, args []string) { From 8e796ceeed0c0eb7a03506e9e2def83430714011 Mon Sep 17 00:00:00 2001 From: Arthur Amstutz Date: Wed, 18 Feb 2026 14:39:53 +0000 Subject: [PATCH 3/3] doc: Add loadbalancer new commands Signed-off-by: Arthur Amstutz --- doc/ovhcloud_cloud_loadbalancer.md | 10 + ...loud_loadbalancer_associate-floating-ip.md | 43 ++ ...d_cloud_loadbalancer_create-floating-ip.md | 42 ++ doc/ovhcloud_cloud_loadbalancer_create.md | 73 ++ doc/ovhcloud_cloud_loadbalancer_delete.md | 37 ++ ...cloud_cloud_loadbalancer_health-monitor.md | 38 ++ ...loud_loadbalancer_health-monitor_create.md | 48 ++ ...loud_loadbalancer_health-monitor_delete.md | 37 ++ ..._cloud_loadbalancer_health-monitor_edit.md | 43 ++ ...d_cloud_loadbalancer_health-monitor_get.md | 37 ++ ..._cloud_loadbalancer_health-monitor_list.md | 44 ++ doc/ovhcloud_cloud_loadbalancer_l7policy.md | 39 ++ ...loud_cloud_loadbalancer_l7policy_create.md | 49 ++ ...loud_cloud_loadbalancer_l7policy_delete.md | 37 ++ ...hcloud_cloud_loadbalancer_l7policy_edit.md | 46 ++ ...vhcloud_cloud_loadbalancer_l7policy_get.md | 37 ++ ...loud_cloud_loadbalancer_l7policy_l7rule.md | 38 ++ ...oud_loadbalancer_l7policy_l7rule_create.md | 45 ++ ...oud_loadbalancer_l7policy_l7rule_delete.md | 37 ++ ...cloud_loadbalancer_l7policy_l7rule_edit.md | 42 ++ ..._cloud_loadbalancer_l7policy_l7rule_get.md | 37 ++ ...cloud_loadbalancer_l7policy_l7rule_list.md | 44 ++ ...hcloud_cloud_loadbalancer_l7policy_list.md | 44 ++ doc/ovhcloud_cloud_loadbalancer_listener.md | 38 ++ ...loud_cloud_loadbalancer_listener_create.md | 45 ++ ...loud_cloud_loadbalancer_listener_delete.md | 37 ++ ...hcloud_cloud_loadbalancer_listener_edit.md | 42 ++ ...vhcloud_cloud_loadbalancer_listener_get.md | 37 ++ ...hcloud_cloud_loadbalancer_listener_list.md | 44 ++ doc/ovhcloud_cloud_loadbalancer_log.md | 37 ++ ...oud_cloud_loadbalancer_log_generate-url.md | 38 ++ ...vhcloud_cloud_loadbalancer_log_get-kind.md | 37 ++ ...cloud_cloud_loadbalancer_log_list-kinds.md | 44 ++ ...oud_cloud_loadbalancer_log_subscription.md | 37 ++ ...ud_loadbalancer_log_subscription_create.md | 43 ++ ...ud_loadbalancer_log_subscription_delete.md | 37 ++ ...cloud_loadbalancer_log_subscription_get.md | 37 ++ ...loud_loadbalancer_log_subscription_list.md | 44 ++ doc/ovhcloud_cloud_loadbalancer_pool.md | 39 ++ ...ovhcloud_cloud_loadbalancer_pool_create.md | 46 ++ ...ovhcloud_cloud_loadbalancer_pool_delete.md | 37 ++ doc/ovhcloud_cloud_loadbalancer_pool_edit.md | 40 ++ doc/ovhcloud_cloud_loadbalancer_pool_get.md | 37 ++ doc/ovhcloud_cloud_loadbalancer_pool_list.md | 44 ++ ...ovhcloud_cloud_loadbalancer_pool_member.md | 38 ++ ...d_cloud_loadbalancer_pool_member_create.md | 41 ++ ...d_cloud_loadbalancer_pool_member_delete.md | 37 ++ ...oud_cloud_loadbalancer_pool_member_edit.md | 40 ++ ...loud_cloud_loadbalancer_pool_member_get.md | 37 ++ ...oud_cloud_loadbalancer_pool_member_list.md | 44 ++ doc/ovhcloud_cloud_loadbalancer_stats.md | 37 ++ doc/ovhcloud_cloud_reference_loadbalancer.md | 1 + ...cloud_reference_loadbalancer_get-flavor.md | 37 ++ internal/cmd/cloud_loadbalancer.go | 610 +++++++++++++++++ ...cer_test.go => cloud_loadbalancer_test.go} | 162 ++++- internal/cmd/cloud_network_loadbalancer.go | 622 ------------------ internal/cmd/cloud_network_test.go | 103 --- internal/services/browser/api.go | 2 +- 58 files changed, 2845 insertions(+), 753 deletions(-) create mode 100644 doc/ovhcloud_cloud_loadbalancer_associate-floating-ip.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_create-floating-ip.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_create.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_delete.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_health-monitor.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_health-monitor_create.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_health-monitor_delete.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_health-monitor_edit.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_health-monitor_get.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_health-monitor_list.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_l7policy.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_l7policy_create.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_l7policy_delete.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_l7policy_edit.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_l7policy_get.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_create.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_delete.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_edit.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_get.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_list.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_l7policy_list.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_listener.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_listener_create.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_listener_delete.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_listener_edit.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_listener_get.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_listener_list.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_log.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_log_generate-url.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_log_get-kind.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_log_list-kinds.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_log_subscription.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_log_subscription_create.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_log_subscription_delete.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_log_subscription_get.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_log_subscription_list.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_pool.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_pool_create.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_pool_delete.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_pool_edit.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_pool_get.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_pool_list.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_pool_member.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_pool_member_create.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_pool_member_delete.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_pool_member_edit.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_pool_member_get.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_pool_member_list.md create mode 100644 doc/ovhcloud_cloud_loadbalancer_stats.md create mode 100644 doc/ovhcloud_cloud_reference_loadbalancer_get-flavor.md rename internal/cmd/{cloud_network_loadbalancer_test.go => cloud_loadbalancer_test.go} (81%) delete mode 100644 internal/cmd/cloud_network_loadbalancer.go diff --git a/doc/ovhcloud_cloud_loadbalancer.md b/doc/ovhcloud_cloud_loadbalancer.md index 33ee49bb..c52aee64 100644 --- a/doc/ovhcloud_cloud_loadbalancer.md +++ b/doc/ovhcloud_cloud_loadbalancer.md @@ -30,7 +30,17 @@ Manage loadbalancers in the given cloud project ### SEE ALSO * [ovhcloud cloud](ovhcloud_cloud.md) - Manage your projects and services in the Public Cloud universe (MKS, MPR, MRS, Object Storage...) +* [ovhcloud cloud loadbalancer associate-floating-ip](ovhcloud_cloud_loadbalancer_associate-floating-ip.md) - Associate an existing floating IP to a loadbalancer +* [ovhcloud cloud loadbalancer create](ovhcloud_cloud_loadbalancer_create.md) - Create a loadbalancer in the given cloud project +* [ovhcloud cloud loadbalancer create-floating-ip](ovhcloud_cloud_loadbalancer_create-floating-ip.md) - Create a floating IP and attach it to a loadbalancer +* [ovhcloud cloud loadbalancer delete](ovhcloud_cloud_loadbalancer_delete.md) - Delete a specific loadbalancer * [ovhcloud cloud loadbalancer edit](ovhcloud_cloud_loadbalancer_edit.md) - Edit the given loadbalancer * [ovhcloud cloud loadbalancer get](ovhcloud_cloud_loadbalancer_get.md) - Get a specific loadbalancer +* [ovhcloud cloud loadbalancer health-monitor](ovhcloud_cloud_loadbalancer_health-monitor.md) - Manage health monitors of loadbalancers +* [ovhcloud cloud loadbalancer l7policy](ovhcloud_cloud_loadbalancer_l7policy.md) - Manage L7 policies of loadbalancers * [ovhcloud cloud loadbalancer list](ovhcloud_cloud_loadbalancer_list.md) - List your loadbalancers +* [ovhcloud cloud loadbalancer listener](ovhcloud_cloud_loadbalancer_listener.md) - Manage listeners of loadbalancers +* [ovhcloud cloud loadbalancer log](ovhcloud_cloud_loadbalancer_log.md) - Manage loadbalancer logs +* [ovhcloud cloud loadbalancer pool](ovhcloud_cloud_loadbalancer_pool.md) - Manage pools of loadbalancers +* [ovhcloud cloud loadbalancer stats](ovhcloud_cloud_loadbalancer_stats.md) - Get statistics for a loadbalancer diff --git a/doc/ovhcloud_cloud_loadbalancer_associate-floating-ip.md b/doc/ovhcloud_cloud_loadbalancer_associate-floating-ip.md new file mode 100644 index 00000000..c11ef7bb --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_associate-floating-ip.md @@ -0,0 +1,43 @@ +## ovhcloud cloud loadbalancer associate-floating-ip + +Associate an existing floating IP to a loadbalancer + +``` +ovhcloud cloud loadbalancer associate-floating-ip [flags] +``` + +### Options + +``` + --editor Use a text editor to define parameters + --floating-ip-id string Floating IP ID + --from-file string File containing parameters + -h, --help help for associate-floating-ip + --init-file string Create a file with example parameters + --ip string Private loadbalancer IP to associate the floating IP with + --replace Replace parameters file if it already exists +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer](ovhcloud_cloud_loadbalancer.md) - Manage loadbalancers in the given cloud project + diff --git a/doc/ovhcloud_cloud_loadbalancer_create-floating-ip.md b/doc/ovhcloud_cloud_loadbalancer_create-floating-ip.md new file mode 100644 index 00000000..070960e0 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_create-floating-ip.md @@ -0,0 +1,42 @@ +## ovhcloud cloud loadbalancer create-floating-ip + +Create a floating IP and attach it to a loadbalancer + +``` +ovhcloud cloud loadbalancer create-floating-ip [flags] +``` + +### Options + +``` + --editor Use a text editor to define parameters + --from-file string File containing parameters + -h, --help help for create-floating-ip + --init-file string Create a file with example parameters + --ip string Private loadbalancer IP to associate the floating IP with + --replace Replace parameters file if it already exists +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer](ovhcloud_cloud_loadbalancer.md) - Manage loadbalancers in the given cloud project + diff --git a/doc/ovhcloud_cloud_loadbalancer_create.md b/doc/ovhcloud_cloud_loadbalancer_create.md new file mode 100644 index 00000000..6d027686 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_create.md @@ -0,0 +1,73 @@ +## ovhcloud cloud loadbalancer create + +Create a loadbalancer in the given cloud project + +### Synopsis + +Use this command to create a loadbalancer. +There are three ways to define the parameters: + +1. Using a configuration file: + + First you can generate an example of parameters file using the following command: + + ovhcloud cloud loadbalancer create --init-file ./params.json + + After editing the file to set the correct creation parameters, run: + + ovhcloud cloud loadbalancer create --from-file ./params.json + + Note that you can also pipe the content of the parameters file. + +2. Using your default text editor: + + ovhcloud cloud loadbalancer create --editor + +3. Using only CLI flags: + + ovhcloud cloud loadbalancer create --name my-lb --flavor + + +``` +ovhcloud cloud loadbalancer create [flags] +``` + +### Options + +``` + --editor Use a text editor to define parameters + --flavor string Flavor ID (can be retrieved with 'cloud reference loadbalancer list-flavors ') + --floating-ip string Floating IP ID to associate to the loadbalancer + --from-file string File containing parameters + --gateway string Gateway ID to associate to the loadbalancer + -h, --help help for create + --init-file string Create a file with example parameters + --name string Name of the loadbalancer + --network-id string Network ID + --replace Replace parameters file if it already exists + --subnet-id string Subnet ID +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer](ovhcloud_cloud_loadbalancer.md) - Manage loadbalancers in the given cloud project + diff --git a/doc/ovhcloud_cloud_loadbalancer_delete.md b/doc/ovhcloud_cloud_loadbalancer_delete.md new file mode 100644 index 00000000..b09b9ea0 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_delete.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer delete + +Delete a specific loadbalancer + +``` +ovhcloud cloud loadbalancer delete [flags] +``` + +### Options + +``` + -h, --help help for delete +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer](ovhcloud_cloud_loadbalancer.md) - Manage loadbalancers in the given cloud project + diff --git a/doc/ovhcloud_cloud_loadbalancer_health-monitor.md b/doc/ovhcloud_cloud_loadbalancer_health-monitor.md new file mode 100644 index 00000000..0cd1a312 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_health-monitor.md @@ -0,0 +1,38 @@ +## ovhcloud cloud loadbalancer health-monitor + +Manage health monitors of loadbalancers + +### Options + +``` + -h, --help help for health-monitor +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer](ovhcloud_cloud_loadbalancer.md) - Manage loadbalancers in the given cloud project +* [ovhcloud cloud loadbalancer health-monitor create](ovhcloud_cloud_loadbalancer_health-monitor_create.md) - Create a health monitor in the given region +* [ovhcloud cloud loadbalancer health-monitor delete](ovhcloud_cloud_loadbalancer_health-monitor_delete.md) - Delete a specific health monitor +* [ovhcloud cloud loadbalancer health-monitor edit](ovhcloud_cloud_loadbalancer_health-monitor_edit.md) - Edit a specific health monitor +* [ovhcloud cloud loadbalancer health-monitor get](ovhcloud_cloud_loadbalancer_health-monitor_get.md) - Get a specific health monitor +* [ovhcloud cloud loadbalancer health-monitor list](ovhcloud_cloud_loadbalancer_health-monitor_list.md) - List all health monitors + diff --git a/doc/ovhcloud_cloud_loadbalancer_health-monitor_create.md b/doc/ovhcloud_cloud_loadbalancer_health-monitor_create.md new file mode 100644 index 00000000..9bac2c73 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_health-monitor_create.md @@ -0,0 +1,48 @@ +## ovhcloud cloud loadbalancer health-monitor create + +Create a health monitor in the given region + +``` +ovhcloud cloud loadbalancer health-monitor create [flags] +``` + +### Options + +``` + --delay int Duration between sending probes to members, in seconds + --editor Use a text editor to define parameters + --from-file string File containing parameters + -h, --help help for create + --init-file string Create a file with example parameters + --max-retries int Number of successful checks before changing status to ONLINE + --max-retries-down int Number of allowed check failures before changing status to ERROR + --monitor-type string Type of the monitor (http, https, ping, tcp, tls-hello, udp-connect, sctp) + --name string Name of the health monitor + --pool-id string Pool ID + --replace Replace parameters file if it already exists + --timeout int Maximum time in seconds to connect before timeout +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer health-monitor](ovhcloud_cloud_loadbalancer_health-monitor.md) - Manage health monitors of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_health-monitor_delete.md b/doc/ovhcloud_cloud_loadbalancer_health-monitor_delete.md new file mode 100644 index 00000000..e8fcd0eb --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_health-monitor_delete.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer health-monitor delete + +Delete a specific health monitor + +``` +ovhcloud cloud loadbalancer health-monitor delete [flags] +``` + +### Options + +``` + -h, --help help for delete +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer health-monitor](ovhcloud_cloud_loadbalancer_health-monitor.md) - Manage health monitors of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_health-monitor_edit.md b/doc/ovhcloud_cloud_loadbalancer_health-monitor_edit.md new file mode 100644 index 00000000..5da328db --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_health-monitor_edit.md @@ -0,0 +1,43 @@ +## ovhcloud cloud loadbalancer health-monitor edit + +Edit a specific health monitor + +``` +ovhcloud cloud loadbalancer health-monitor edit [flags] +``` + +### Options + +``` + --delay int Duration between sending probes to members, in seconds + --editor Use a text editor to define parameters + -h, --help help for edit + --max-retries int Number of successful checks before changing status to ONLINE + --max-retries-down int Number of allowed check failures before changing status to ERROR + --name string Name of the health monitor + --timeout int Maximum time in seconds to connect before timeout +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer health-monitor](ovhcloud_cloud_loadbalancer_health-monitor.md) - Manage health monitors of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_health-monitor_get.md b/doc/ovhcloud_cloud_loadbalancer_health-monitor_get.md new file mode 100644 index 00000000..b702f8b0 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_health-monitor_get.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer health-monitor get + +Get a specific health monitor + +``` +ovhcloud cloud loadbalancer health-monitor get [flags] +``` + +### Options + +``` + -h, --help help for get +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer health-monitor](ovhcloud_cloud_loadbalancer_health-monitor.md) - Manage health monitors of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_health-monitor_list.md b/doc/ovhcloud_cloud_loadbalancer_health-monitor_list.md new file mode 100644 index 00000000..ff3a9fb7 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_health-monitor_list.md @@ -0,0 +1,44 @@ +## ovhcloud cloud loadbalancer health-monitor list + +List all health monitors + +``` +ovhcloud cloud loadbalancer health-monitor list [flags] +``` + +### Options + +``` + --filter stringArray Filter results by any property using https://github.com/PaesslerAG/gval syntax + Examples: + --filter 'state="running"' + --filter 'name=~"^my.*"' + --filter 'nested.property.subproperty>10' + --filter 'startDate>="2023-12-01"' + --filter 'name=~"something" && nbField>10' + -h, --help help for list +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer health-monitor](ovhcloud_cloud_loadbalancer_health-monitor.md) - Manage health monitors of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_l7policy.md b/doc/ovhcloud_cloud_loadbalancer_l7policy.md new file mode 100644 index 00000000..232af9c0 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_l7policy.md @@ -0,0 +1,39 @@ +## ovhcloud cloud loadbalancer l7policy + +Manage L7 policies of loadbalancers + +### Options + +``` + -h, --help help for l7policy +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer](ovhcloud_cloud_loadbalancer.md) - Manage loadbalancers in the given cloud project +* [ovhcloud cloud loadbalancer l7policy create](ovhcloud_cloud_loadbalancer_l7policy_create.md) - Create an L7 policy in the given region +* [ovhcloud cloud loadbalancer l7policy delete](ovhcloud_cloud_loadbalancer_l7policy_delete.md) - Delete a specific L7 policy +* [ovhcloud cloud loadbalancer l7policy edit](ovhcloud_cloud_loadbalancer_l7policy_edit.md) - Edit a specific L7 policy +* [ovhcloud cloud loadbalancer l7policy get](ovhcloud_cloud_loadbalancer_l7policy_get.md) - Get a specific L7 policy +* [ovhcloud cloud loadbalancer l7policy l7rule](ovhcloud_cloud_loadbalancer_l7policy_l7rule.md) - Manage L7 rules of a loadbalancer L7 policy +* [ovhcloud cloud loadbalancer l7policy list](ovhcloud_cloud_loadbalancer_l7policy_list.md) - List all L7 policies + diff --git a/doc/ovhcloud_cloud_loadbalancer_l7policy_create.md b/doc/ovhcloud_cloud_loadbalancer_l7policy_create.md new file mode 100644 index 00000000..fb7ef8a7 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_l7policy_create.md @@ -0,0 +1,49 @@ +## ovhcloud cloud loadbalancer l7policy create + +Create an L7 policy in the given region + +``` +ovhcloud cloud loadbalancer l7policy create [flags] +``` + +### Options + +``` + --action string L7 policy action (redirectToPool, redirectToUrl, redirectPrefix, reject) + --description string Description + --editor Use a text editor to define parameters + --from-file string File containing parameters + -h, --help help for create + --init-file string Create a file with example parameters + --listener-id string Listener ID + --name string Name of the L7 policy + --position int Position on the listener + --redirect-pool-id string Redirect pool ID + --redirect-prefix string Redirect prefix URL + --redirect-url string Redirect URL + --replace Replace parameters file if it already exists +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer l7policy](ovhcloud_cloud_loadbalancer_l7policy.md) - Manage L7 policies of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_l7policy_delete.md b/doc/ovhcloud_cloud_loadbalancer_l7policy_delete.md new file mode 100644 index 00000000..68788a21 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_l7policy_delete.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer l7policy delete + +Delete a specific L7 policy + +``` +ovhcloud cloud loadbalancer l7policy delete [flags] +``` + +### Options + +``` + -h, --help help for delete +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer l7policy](ovhcloud_cloud_loadbalancer_l7policy.md) - Manage L7 policies of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_l7policy_edit.md b/doc/ovhcloud_cloud_loadbalancer_l7policy_edit.md new file mode 100644 index 00000000..7a82e09d --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_l7policy_edit.md @@ -0,0 +1,46 @@ +## ovhcloud cloud loadbalancer l7policy edit + +Edit a specific L7 policy + +``` +ovhcloud cloud loadbalancer l7policy edit [flags] +``` + +### Options + +``` + --action string L7 policy action + --description string Description + --editor Use a text editor to define parameters + -h, --help help for edit + --listener-id string Listener ID + --name string Name of the L7 policy + --position int Position on the listener + --redirect-pool-id string Redirect pool ID + --redirect-prefix string Redirect prefix URL + --redirect-url string Redirect URL +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer l7policy](ovhcloud_cloud_loadbalancer_l7policy.md) - Manage L7 policies of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_l7policy_get.md b/doc/ovhcloud_cloud_loadbalancer_l7policy_get.md new file mode 100644 index 00000000..22a4efe1 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_l7policy_get.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer l7policy get + +Get a specific L7 policy + +``` +ovhcloud cloud loadbalancer l7policy get [flags] +``` + +### Options + +``` + -h, --help help for get +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer l7policy](ovhcloud_cloud_loadbalancer_l7policy.md) - Manage L7 policies of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule.md b/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule.md new file mode 100644 index 00000000..fde956c9 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule.md @@ -0,0 +1,38 @@ +## ovhcloud cloud loadbalancer l7policy l7rule + +Manage L7 rules of a loadbalancer L7 policy + +### Options + +``` + -h, --help help for l7rule +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer l7policy](ovhcloud_cloud_loadbalancer_l7policy.md) - Manage L7 policies of loadbalancers +* [ovhcloud cloud loadbalancer l7policy l7rule create](ovhcloud_cloud_loadbalancer_l7policy_l7rule_create.md) - Create an L7 rule in a specific L7 policy +* [ovhcloud cloud loadbalancer l7policy l7rule delete](ovhcloud_cloud_loadbalancer_l7policy_l7rule_delete.md) - Delete a specific L7 rule +* [ovhcloud cloud loadbalancer l7policy l7rule edit](ovhcloud_cloud_loadbalancer_l7policy_l7rule_edit.md) - Edit a specific L7 rule +* [ovhcloud cloud loadbalancer l7policy l7rule get](ovhcloud_cloud_loadbalancer_l7policy_l7rule_get.md) - Get a specific L7 rule +* [ovhcloud cloud loadbalancer l7policy l7rule list](ovhcloud_cloud_loadbalancer_l7policy_l7rule_list.md) - List L7 rules of a specific L7 policy + diff --git a/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_create.md b/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_create.md new file mode 100644 index 00000000..cfde46d3 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_create.md @@ -0,0 +1,45 @@ +## ovhcloud cloud loadbalancer l7policy l7rule create + +Create an L7 rule in a specific L7 policy + +``` +ovhcloud cloud loadbalancer l7policy l7rule create [flags] +``` + +### Options + +``` + --compare-type string Comparison type (contains, endsWith, equalTo, regex, startsWith) + --editor Use a text editor to define parameters + --from-file string File containing parameters + -h, --help help for create + --init-file string Create a file with example parameters + --key string Key to use for comparison + --replace Replace parameters file if it already exists + --rule-type string Rule type (cookie, fileType, header, hostName, path, sslConnHasCert, sslDNField, sslVerifyResult) + --value string Value to compare +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer l7policy l7rule](ovhcloud_cloud_loadbalancer_l7policy_l7rule.md) - Manage L7 rules of a loadbalancer L7 policy + diff --git a/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_delete.md b/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_delete.md new file mode 100644 index 00000000..789ec715 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_delete.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer l7policy l7rule delete + +Delete a specific L7 rule + +``` +ovhcloud cloud loadbalancer l7policy l7rule delete [flags] +``` + +### Options + +``` + -h, --help help for delete +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer l7policy l7rule](ovhcloud_cloud_loadbalancer_l7policy_l7rule.md) - Manage L7 rules of a loadbalancer L7 policy + diff --git a/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_edit.md b/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_edit.md new file mode 100644 index 00000000..3739405d --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_edit.md @@ -0,0 +1,42 @@ +## ovhcloud cloud loadbalancer l7policy l7rule edit + +Edit a specific L7 rule + +``` +ovhcloud cloud loadbalancer l7policy l7rule edit [flags] +``` + +### Options + +``` + --compare-type string Comparison type + --editor Use a text editor to define parameters + -h, --help help for edit + --key string Key to use for comparison + --rule-type string Rule type + --value string Value to compare +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer l7policy l7rule](ovhcloud_cloud_loadbalancer_l7policy_l7rule.md) - Manage L7 rules of a loadbalancer L7 policy + diff --git a/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_get.md b/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_get.md new file mode 100644 index 00000000..1fcba0d1 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_get.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer l7policy l7rule get + +Get a specific L7 rule + +``` +ovhcloud cloud loadbalancer l7policy l7rule get [flags] +``` + +### Options + +``` + -h, --help help for get +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer l7policy l7rule](ovhcloud_cloud_loadbalancer_l7policy_l7rule.md) - Manage L7 rules of a loadbalancer L7 policy + diff --git a/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_list.md b/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_list.md new file mode 100644 index 00000000..17fb27f4 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_l7policy_l7rule_list.md @@ -0,0 +1,44 @@ +## ovhcloud cloud loadbalancer l7policy l7rule list + +List L7 rules of a specific L7 policy + +``` +ovhcloud cloud loadbalancer l7policy l7rule list [flags] +``` + +### Options + +``` + --filter stringArray Filter results by any property using https://github.com/PaesslerAG/gval syntax + Examples: + --filter 'state="running"' + --filter 'name=~"^my.*"' + --filter 'nested.property.subproperty>10' + --filter 'startDate>="2023-12-01"' + --filter 'name=~"something" && nbField>10' + -h, --help help for list +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer l7policy l7rule](ovhcloud_cloud_loadbalancer_l7policy_l7rule.md) - Manage L7 rules of a loadbalancer L7 policy + diff --git a/doc/ovhcloud_cloud_loadbalancer_l7policy_list.md b/doc/ovhcloud_cloud_loadbalancer_l7policy_list.md new file mode 100644 index 00000000..8f8085b2 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_l7policy_list.md @@ -0,0 +1,44 @@ +## ovhcloud cloud loadbalancer l7policy list + +List all L7 policies + +``` +ovhcloud cloud loadbalancer l7policy list [flags] +``` + +### Options + +``` + --filter stringArray Filter results by any property using https://github.com/PaesslerAG/gval syntax + Examples: + --filter 'state="running"' + --filter 'name=~"^my.*"' + --filter 'nested.property.subproperty>10' + --filter 'startDate>="2023-12-01"' + --filter 'name=~"something" && nbField>10' + -h, --help help for list +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer l7policy](ovhcloud_cloud_loadbalancer_l7policy.md) - Manage L7 policies of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_listener.md b/doc/ovhcloud_cloud_loadbalancer_listener.md new file mode 100644 index 00000000..fbecb85e --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_listener.md @@ -0,0 +1,38 @@ +## ovhcloud cloud loadbalancer listener + +Manage listeners of loadbalancers + +### Options + +``` + -h, --help help for listener +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer](ovhcloud_cloud_loadbalancer.md) - Manage loadbalancers in the given cloud project +* [ovhcloud cloud loadbalancer listener create](ovhcloud_cloud_loadbalancer_listener_create.md) - Create a listener in the given region +* [ovhcloud cloud loadbalancer listener delete](ovhcloud_cloud_loadbalancer_listener_delete.md) - Delete a specific listener +* [ovhcloud cloud loadbalancer listener edit](ovhcloud_cloud_loadbalancer_listener_edit.md) - Edit a specific listener +* [ovhcloud cloud loadbalancer listener get](ovhcloud_cloud_loadbalancer_listener_get.md) - Get a specific listener +* [ovhcloud cloud loadbalancer listener list](ovhcloud_cloud_loadbalancer_listener_list.md) - List all listeners + diff --git a/doc/ovhcloud_cloud_loadbalancer_listener_create.md b/doc/ovhcloud_cloud_loadbalancer_listener_create.md new file mode 100644 index 00000000..ae11a972 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_listener_create.md @@ -0,0 +1,45 @@ +## ovhcloud cloud loadbalancer listener create + +Create a listener in the given region + +``` +ovhcloud cloud loadbalancer listener create [flags] +``` + +### Options + +``` + --editor Use a text editor to define parameters + --from-file string File containing parameters + -h, --help help for create + --init-file string Create a file with example parameters + --loadbalancer-id string Loadbalancer ID + --name string Name of the listener + --port int Port to listen on + --protocol string Protocol (http, https, tcp, udp, sctp, prometheus) + --replace Replace parameters file if it already exists +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer listener](ovhcloud_cloud_loadbalancer_listener.md) - Manage listeners of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_listener_delete.md b/doc/ovhcloud_cloud_loadbalancer_listener_delete.md new file mode 100644 index 00000000..6d564a82 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_listener_delete.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer listener delete + +Delete a specific listener + +``` +ovhcloud cloud loadbalancer listener delete [flags] +``` + +### Options + +``` + -h, --help help for delete +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer listener](ovhcloud_cloud_loadbalancer_listener.md) - Manage listeners of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_listener_edit.md b/doc/ovhcloud_cloud_loadbalancer_listener_edit.md new file mode 100644 index 00000000..d95aad31 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_listener_edit.md @@ -0,0 +1,42 @@ +## ovhcloud cloud loadbalancer listener edit + +Edit a specific listener + +``` +ovhcloud cloud loadbalancer listener edit [flags] +``` + +### Options + +``` + --certificate-id string Certificate ID + --default-pool-id string Default pool ID + --description string Description of the listener + --editor Use a text editor to define parameters + -h, --help help for edit + --name string Name of the listener +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer listener](ovhcloud_cloud_loadbalancer_listener.md) - Manage listeners of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_listener_get.md b/doc/ovhcloud_cloud_loadbalancer_listener_get.md new file mode 100644 index 00000000..edf2f451 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_listener_get.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer listener get + +Get a specific listener + +``` +ovhcloud cloud loadbalancer listener get [flags] +``` + +### Options + +``` + -h, --help help for get +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer listener](ovhcloud_cloud_loadbalancer_listener.md) - Manage listeners of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_listener_list.md b/doc/ovhcloud_cloud_loadbalancer_listener_list.md new file mode 100644 index 00000000..2d2ba583 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_listener_list.md @@ -0,0 +1,44 @@ +## ovhcloud cloud loadbalancer listener list + +List all listeners + +``` +ovhcloud cloud loadbalancer listener list [flags] +``` + +### Options + +``` + --filter stringArray Filter results by any property using https://github.com/PaesslerAG/gval syntax + Examples: + --filter 'state="running"' + --filter 'name=~"^my.*"' + --filter 'nested.property.subproperty>10' + --filter 'startDate>="2023-12-01"' + --filter 'name=~"something" && nbField>10' + -h, --help help for list +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer listener](ovhcloud_cloud_loadbalancer_listener.md) - Manage listeners of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_log.md b/doc/ovhcloud_cloud_loadbalancer_log.md new file mode 100644 index 00000000..bacb9c83 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_log.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer log + +Manage loadbalancer logs + +### Options + +``` + -h, --help help for log +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer](ovhcloud_cloud_loadbalancer.md) - Manage loadbalancers in the given cloud project +* [ovhcloud cloud loadbalancer log generate-url](ovhcloud_cloud_loadbalancer_log_generate-url.md) - Generate a temporary URL to retrieve logs +* [ovhcloud cloud loadbalancer log get-kind](ovhcloud_cloud_loadbalancer_log_get-kind.md) - Get a specific log kind +* [ovhcloud cloud loadbalancer log list-kinds](ovhcloud_cloud_loadbalancer_log_list-kinds.md) - List available log kinds +* [ovhcloud cloud loadbalancer log subscription](ovhcloud_cloud_loadbalancer_log_subscription.md) - Manage log subscriptions for a loadbalancer + diff --git a/doc/ovhcloud_cloud_loadbalancer_log_generate-url.md b/doc/ovhcloud_cloud_loadbalancer_log_generate-url.md new file mode 100644 index 00000000..3ad56f87 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_log_generate-url.md @@ -0,0 +1,38 @@ +## ovhcloud cloud loadbalancer log generate-url + +Generate a temporary URL to retrieve logs + +``` +ovhcloud cloud loadbalancer log generate-url [flags] +``` + +### Options + +``` + -h, --help help for generate-url + --kind string Log kind (e.g., haproxy) +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer log](ovhcloud_cloud_loadbalancer_log.md) - Manage loadbalancer logs + diff --git a/doc/ovhcloud_cloud_loadbalancer_log_get-kind.md b/doc/ovhcloud_cloud_loadbalancer_log_get-kind.md new file mode 100644 index 00000000..2ddc5445 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_log_get-kind.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer log get-kind + +Get a specific log kind + +``` +ovhcloud cloud loadbalancer log get-kind [flags] +``` + +### Options + +``` + -h, --help help for get-kind +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer log](ovhcloud_cloud_loadbalancer_log.md) - Manage loadbalancer logs + diff --git a/doc/ovhcloud_cloud_loadbalancer_log_list-kinds.md b/doc/ovhcloud_cloud_loadbalancer_log_list-kinds.md new file mode 100644 index 00000000..4ef4eca4 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_log_list-kinds.md @@ -0,0 +1,44 @@ +## ovhcloud cloud loadbalancer log list-kinds + +List available log kinds + +``` +ovhcloud cloud loadbalancer log list-kinds [flags] +``` + +### Options + +``` + --filter stringArray Filter results by any property using https://github.com/PaesslerAG/gval syntax + Examples: + --filter 'state="running"' + --filter 'name=~"^my.*"' + --filter 'nested.property.subproperty>10' + --filter 'startDate>="2023-12-01"' + --filter 'name=~"something" && nbField>10' + -h, --help help for list-kinds +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer log](ovhcloud_cloud_loadbalancer_log.md) - Manage loadbalancer logs + diff --git a/doc/ovhcloud_cloud_loadbalancer_log_subscription.md b/doc/ovhcloud_cloud_loadbalancer_log_subscription.md new file mode 100644 index 00000000..4fc79f24 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_log_subscription.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer log subscription + +Manage log subscriptions for a loadbalancer + +### Options + +``` + -h, --help help for subscription +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer log](ovhcloud_cloud_loadbalancer_log.md) - Manage loadbalancer logs +* [ovhcloud cloud loadbalancer log subscription create](ovhcloud_cloud_loadbalancer_log_subscription_create.md) - Create a log subscription for a loadbalancer +* [ovhcloud cloud loadbalancer log subscription delete](ovhcloud_cloud_loadbalancer_log_subscription_delete.md) - Delete a log subscription +* [ovhcloud cloud loadbalancer log subscription get](ovhcloud_cloud_loadbalancer_log_subscription_get.md) - Get a specific log subscription +* [ovhcloud cloud loadbalancer log subscription list](ovhcloud_cloud_loadbalancer_log_subscription_list.md) - List log subscriptions of a loadbalancer + diff --git a/doc/ovhcloud_cloud_loadbalancer_log_subscription_create.md b/doc/ovhcloud_cloud_loadbalancer_log_subscription_create.md new file mode 100644 index 00000000..9d86c9c1 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_log_subscription_create.md @@ -0,0 +1,43 @@ +## ovhcloud cloud loadbalancer log subscription create + +Create a log subscription for a loadbalancer + +``` +ovhcloud cloud loadbalancer log subscription create [flags] +``` + +### Options + +``` + --editor Use a text editor to define parameters + --from-file string File containing parameters + -h, --help help for create + --init-file string Create a file with example parameters + --kind string Log kind (e.g., haproxy) + --replace Replace parameters file if it already exists + --stream-id string LDP stream ID +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer log subscription](ovhcloud_cloud_loadbalancer_log_subscription.md) - Manage log subscriptions for a loadbalancer + diff --git a/doc/ovhcloud_cloud_loadbalancer_log_subscription_delete.md b/doc/ovhcloud_cloud_loadbalancer_log_subscription_delete.md new file mode 100644 index 00000000..164de7bf --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_log_subscription_delete.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer log subscription delete + +Delete a log subscription + +``` +ovhcloud cloud loadbalancer log subscription delete [flags] +``` + +### Options + +``` + -h, --help help for delete +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer log subscription](ovhcloud_cloud_loadbalancer_log_subscription.md) - Manage log subscriptions for a loadbalancer + diff --git a/doc/ovhcloud_cloud_loadbalancer_log_subscription_get.md b/doc/ovhcloud_cloud_loadbalancer_log_subscription_get.md new file mode 100644 index 00000000..0c4e8aee --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_log_subscription_get.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer log subscription get + +Get a specific log subscription + +``` +ovhcloud cloud loadbalancer log subscription get [flags] +``` + +### Options + +``` + -h, --help help for get +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer log subscription](ovhcloud_cloud_loadbalancer_log_subscription.md) - Manage log subscriptions for a loadbalancer + diff --git a/doc/ovhcloud_cloud_loadbalancer_log_subscription_list.md b/doc/ovhcloud_cloud_loadbalancer_log_subscription_list.md new file mode 100644 index 00000000..e6a41882 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_log_subscription_list.md @@ -0,0 +1,44 @@ +## ovhcloud cloud loadbalancer log subscription list + +List log subscriptions of a loadbalancer + +``` +ovhcloud cloud loadbalancer log subscription list [flags] +``` + +### Options + +``` + --filter stringArray Filter results by any property using https://github.com/PaesslerAG/gval syntax + Examples: + --filter 'state="running"' + --filter 'name=~"^my.*"' + --filter 'nested.property.subproperty>10' + --filter 'startDate>="2023-12-01"' + --filter 'name=~"something" && nbField>10' + -h, --help help for list +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer log subscription](ovhcloud_cloud_loadbalancer_log_subscription.md) - Manage log subscriptions for a loadbalancer + diff --git a/doc/ovhcloud_cloud_loadbalancer_pool.md b/doc/ovhcloud_cloud_loadbalancer_pool.md new file mode 100644 index 00000000..4fbf6d20 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_pool.md @@ -0,0 +1,39 @@ +## ovhcloud cloud loadbalancer pool + +Manage pools of loadbalancers + +### Options + +``` + -h, --help help for pool +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer](ovhcloud_cloud_loadbalancer.md) - Manage loadbalancers in the given cloud project +* [ovhcloud cloud loadbalancer pool create](ovhcloud_cloud_loadbalancer_pool_create.md) - Create a pool in the given region +* [ovhcloud cloud loadbalancer pool delete](ovhcloud_cloud_loadbalancer_pool_delete.md) - Delete a specific pool +* [ovhcloud cloud loadbalancer pool edit](ovhcloud_cloud_loadbalancer_pool_edit.md) - Edit a specific pool +* [ovhcloud cloud loadbalancer pool get](ovhcloud_cloud_loadbalancer_pool_get.md) - Get a specific pool +* [ovhcloud cloud loadbalancer pool list](ovhcloud_cloud_loadbalancer_pool_list.md) - List all pools +* [ovhcloud cloud loadbalancer pool member](ovhcloud_cloud_loadbalancer_pool_member.md) - Manage members of a loadbalancer pool + diff --git a/doc/ovhcloud_cloud_loadbalancer_pool_create.md b/doc/ovhcloud_cloud_loadbalancer_pool_create.md new file mode 100644 index 00000000..748a49cb --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_pool_create.md @@ -0,0 +1,46 @@ +## ovhcloud cloud loadbalancer pool create + +Create a pool in the given region + +``` +ovhcloud cloud loadbalancer pool create [flags] +``` + +### Options + +``` + --algorithm string Algorithm (roundRobin, leastConnections, sourceIp) + --editor Use a text editor to define parameters + --from-file string File containing parameters + -h, --help help for create + --init-file string Create a file with example parameters + --listener-id string Listener ID + --loadbalancer-id string Loadbalancer ID + --name string Name of the pool + --protocol string Protocol (http, https, tcp, udp, sctp, prometheus) + --replace Replace parameters file if it already exists +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer pool](ovhcloud_cloud_loadbalancer_pool.md) - Manage pools of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_pool_delete.md b/doc/ovhcloud_cloud_loadbalancer_pool_delete.md new file mode 100644 index 00000000..88dd116e --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_pool_delete.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer pool delete + +Delete a specific pool + +``` +ovhcloud cloud loadbalancer pool delete [flags] +``` + +### Options + +``` + -h, --help help for delete +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer pool](ovhcloud_cloud_loadbalancer_pool.md) - Manage pools of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_pool_edit.md b/doc/ovhcloud_cloud_loadbalancer_pool_edit.md new file mode 100644 index 00000000..56168f77 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_pool_edit.md @@ -0,0 +1,40 @@ +## ovhcloud cloud loadbalancer pool edit + +Edit a specific pool + +``` +ovhcloud cloud loadbalancer pool edit [flags] +``` + +### Options + +``` + --algorithm string Algorithm (roundRobin, leastConnections, sourceIp) + --editor Use a text editor to define parameters + -h, --help help for edit + --name string Name of the pool +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer pool](ovhcloud_cloud_loadbalancer_pool.md) - Manage pools of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_pool_get.md b/doc/ovhcloud_cloud_loadbalancer_pool_get.md new file mode 100644 index 00000000..71ab540a --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_pool_get.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer pool get + +Get a specific pool + +``` +ovhcloud cloud loadbalancer pool get [flags] +``` + +### Options + +``` + -h, --help help for get +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer pool](ovhcloud_cloud_loadbalancer_pool.md) - Manage pools of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_pool_list.md b/doc/ovhcloud_cloud_loadbalancer_pool_list.md new file mode 100644 index 00000000..ac96f793 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_pool_list.md @@ -0,0 +1,44 @@ +## ovhcloud cloud loadbalancer pool list + +List all pools + +``` +ovhcloud cloud loadbalancer pool list [flags] +``` + +### Options + +``` + --filter stringArray Filter results by any property using https://github.com/PaesslerAG/gval syntax + Examples: + --filter 'state="running"' + --filter 'name=~"^my.*"' + --filter 'nested.property.subproperty>10' + --filter 'startDate>="2023-12-01"' + --filter 'name=~"something" && nbField>10' + -h, --help help for list +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer pool](ovhcloud_cloud_loadbalancer_pool.md) - Manage pools of loadbalancers + diff --git a/doc/ovhcloud_cloud_loadbalancer_pool_member.md b/doc/ovhcloud_cloud_loadbalancer_pool_member.md new file mode 100644 index 00000000..bb84ff6c --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_pool_member.md @@ -0,0 +1,38 @@ +## ovhcloud cloud loadbalancer pool member + +Manage members of a loadbalancer pool + +### Options + +``` + -h, --help help for member +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer pool](ovhcloud_cloud_loadbalancer_pool.md) - Manage pools of loadbalancers +* [ovhcloud cloud loadbalancer pool member create](ovhcloud_cloud_loadbalancer_pool_member_create.md) - Create member(s) in a specific pool +* [ovhcloud cloud loadbalancer pool member delete](ovhcloud_cloud_loadbalancer_pool_member_delete.md) - Delete a specific pool member +* [ovhcloud cloud loadbalancer pool member edit](ovhcloud_cloud_loadbalancer_pool_member_edit.md) - Edit a specific pool member +* [ovhcloud cloud loadbalancer pool member get](ovhcloud_cloud_loadbalancer_pool_member_get.md) - Get a specific pool member +* [ovhcloud cloud loadbalancer pool member list](ovhcloud_cloud_loadbalancer_pool_member_list.md) - List members of a specific pool + diff --git a/doc/ovhcloud_cloud_loadbalancer_pool_member_create.md b/doc/ovhcloud_cloud_loadbalancer_pool_member_create.md new file mode 100644 index 00000000..69c88060 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_pool_member_create.md @@ -0,0 +1,41 @@ +## ovhcloud cloud loadbalancer pool member create + +Create member(s) in a specific pool + +``` +ovhcloud cloud loadbalancer pool member create [flags] +``` + +### Options + +``` + --editor Use a text editor to define parameters + --from-file string File containing parameters + -h, --help help for create + --init-file string Create a file with example parameters + --replace Replace parameters file if it already exists +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer pool member](ovhcloud_cloud_loadbalancer_pool_member.md) - Manage members of a loadbalancer pool + diff --git a/doc/ovhcloud_cloud_loadbalancer_pool_member_delete.md b/doc/ovhcloud_cloud_loadbalancer_pool_member_delete.md new file mode 100644 index 00000000..102ef0cf --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_pool_member_delete.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer pool member delete + +Delete a specific pool member + +``` +ovhcloud cloud loadbalancer pool member delete [flags] +``` + +### Options + +``` + -h, --help help for delete +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer pool member](ovhcloud_cloud_loadbalancer_pool_member.md) - Manage members of a loadbalancer pool + diff --git a/doc/ovhcloud_cloud_loadbalancer_pool_member_edit.md b/doc/ovhcloud_cloud_loadbalancer_pool_member_edit.md new file mode 100644 index 00000000..9696bbb6 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_pool_member_edit.md @@ -0,0 +1,40 @@ +## ovhcloud cloud loadbalancer pool member edit + +Edit a specific pool member + +``` +ovhcloud cloud loadbalancer pool member edit [flags] +``` + +### Options + +``` + --editor Use a text editor to define parameters + -h, --help help for edit + --name string Name of the member + --weight int Weight of the member (1-256) +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer pool member](ovhcloud_cloud_loadbalancer_pool_member.md) - Manage members of a loadbalancer pool + diff --git a/doc/ovhcloud_cloud_loadbalancer_pool_member_get.md b/doc/ovhcloud_cloud_loadbalancer_pool_member_get.md new file mode 100644 index 00000000..de31fe98 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_pool_member_get.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer pool member get + +Get a specific pool member + +``` +ovhcloud cloud loadbalancer pool member get [flags] +``` + +### Options + +``` + -h, --help help for get +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer pool member](ovhcloud_cloud_loadbalancer_pool_member.md) - Manage members of a loadbalancer pool + diff --git a/doc/ovhcloud_cloud_loadbalancer_pool_member_list.md b/doc/ovhcloud_cloud_loadbalancer_pool_member_list.md new file mode 100644 index 00000000..7c542187 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_pool_member_list.md @@ -0,0 +1,44 @@ +## ovhcloud cloud loadbalancer pool member list + +List members of a specific pool + +``` +ovhcloud cloud loadbalancer pool member list [flags] +``` + +### Options + +``` + --filter stringArray Filter results by any property using https://github.com/PaesslerAG/gval syntax + Examples: + --filter 'state="running"' + --filter 'name=~"^my.*"' + --filter 'nested.property.subproperty>10' + --filter 'startDate>="2023-12-01"' + --filter 'name=~"something" && nbField>10' + -h, --help help for list +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer pool member](ovhcloud_cloud_loadbalancer_pool_member.md) - Manage members of a loadbalancer pool + diff --git a/doc/ovhcloud_cloud_loadbalancer_stats.md b/doc/ovhcloud_cloud_loadbalancer_stats.md new file mode 100644 index 00000000..80793739 --- /dev/null +++ b/doc/ovhcloud_cloud_loadbalancer_stats.md @@ -0,0 +1,37 @@ +## ovhcloud cloud loadbalancer stats + +Get statistics for a loadbalancer + +``` +ovhcloud cloud loadbalancer stats [flags] +``` + +### Options + +``` + -h, --help help for stats +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud loadbalancer](ovhcloud_cloud_loadbalancer.md) - Manage loadbalancers in the given cloud project + diff --git a/doc/ovhcloud_cloud_reference_loadbalancer.md b/doc/ovhcloud_cloud_reference_loadbalancer.md index 261e6dac..aa8601e9 100644 --- a/doc/ovhcloud_cloud_reference_loadbalancer.md +++ b/doc/ovhcloud_cloud_reference_loadbalancer.md @@ -30,5 +30,6 @@ Fetch loadbalancer reference data in the given cloud project ### SEE ALSO * [ovhcloud cloud reference](ovhcloud_cloud_reference.md) - Fetch reference data in the given cloud project +* [ovhcloud cloud reference loadbalancer get-flavor](ovhcloud_cloud_reference_loadbalancer_get-flavor.md) - Get details of a specific loadbalancer flavor * [ovhcloud cloud reference loadbalancer list-flavors](ovhcloud_cloud_reference_loadbalancer_list-flavors.md) - List available loadbalancer flavors in the given cloud project diff --git a/doc/ovhcloud_cloud_reference_loadbalancer_get-flavor.md b/doc/ovhcloud_cloud_reference_loadbalancer_get-flavor.md new file mode 100644 index 00000000..6e9d38b9 --- /dev/null +++ b/doc/ovhcloud_cloud_reference_loadbalancer_get-flavor.md @@ -0,0 +1,37 @@ +## ovhcloud cloud reference loadbalancer get-flavor + +Get details of a specific loadbalancer flavor + +``` +ovhcloud cloud reference loadbalancer get-flavor [flags] +``` + +### Options + +``` + -h, --help help for get-flavor +``` + +### Options inherited from parent commands + +``` + --cloud-project string Cloud project ID + -d, --debug Activate debug mode (will log all HTTP requests details) + -e, --ignore-errors Ignore errors in API calls when it is not fatal to the execution + -o, --output string Output format: json, yaml, interactive, or a custom format expression (using https://github.com/PaesslerAG/gval syntax) + Examples: + --output json + --output yaml + --output interactive + --output 'id' (to extract a single field) + --output 'nested.field.subfield' (to extract a nested field) + --output '[id, "name"]' (to extract multiple fields as an array) + --output '{"newKey": oldKey, "otherKey": nested.field}' (to extract and rename fields in an object) + --output 'name+","+type' (to extract and concatenate fields in a string) + --output '(nbFieldA + nbFieldB) * 10' (to compute values from numeric fields) +``` + +### SEE ALSO + +* [ovhcloud cloud reference loadbalancer](ovhcloud_cloud_reference_loadbalancer.md) - Fetch loadbalancer reference data in the given cloud project + diff --git a/internal/cmd/cloud_loadbalancer.go b/internal/cmd/cloud_loadbalancer.go index cc415f16..a41edd38 100644 --- a/internal/cmd/cloud_loadbalancer.go +++ b/internal/cmd/cloud_loadbalancer.go @@ -5,6 +5,7 @@ package cmd import ( + "github.com/ovh/ovhcloud-cli/internal/assets" "github.com/ovh/ovhcloud-cli/internal/services/cloud" "github.com/spf13/cobra" ) @@ -43,5 +44,614 @@ func initCloudLoadbalancerCommand(cloudCmd *cobra.Command) { addInteractiveEditorFlag(editLoadbalancerCmd) loadbalancerCmd.AddCommand(editLoadbalancerCmd) + // Loadbalancer create + loadbalancerCmd.AddCommand(getLoadbalancerCreationCmd()) + + // Loadbalancer delete + loadbalancerCmd.AddCommand(&cobra.Command{ + Use: "delete ", + Short: "Delete a specific loadbalancer", + Run: cloud.DeleteCloudLoadbalancer, + Args: cobra.ExactArgs(1), + }) + + // Loadbalancer stats + loadbalancerCmd.AddCommand(&cobra.Command{ + Use: "stats ", + Short: "Get statistics for a loadbalancer", + Run: cloud.GetCloudLoadbalancerStats, + Args: cobra.ExactArgs(1), + }) + + // Associate floating IP + loadbalancerCmd.AddCommand(getLoadbalancerAssociateFloatingIpCmd()) + + // Create floating IP + loadbalancerCmd.AddCommand(getLoadbalancerCreateFloatingIpCmd()) + + // Listener sub-commands + initListenerSubCommands(loadbalancerCmd) + + // Pool sub-commands + initPoolSubCommands(loadbalancerCmd) + + // Health Monitor sub-commands + initHealthMonitorSubCommands(loadbalancerCmd) + + // L7 Policy sub-commands + initL7PolicySubCommands(loadbalancerCmd) + + // Log sub-commands + initLogSubCommands(loadbalancerCmd) + cloudCmd.AddCommand(loadbalancerCmd) } + +// Loadbalancer creation command helpers + +func getLoadbalancerCreationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create ", + Short: "Create a loadbalancer in the given cloud project", + Long: `Use this command to create a loadbalancer. +There are three ways to define the parameters: + +1. Using a configuration file: + + First you can generate an example of parameters file using the following command: + + ovhcloud cloud loadbalancer create --init-file ./params.json + + After editing the file to set the correct creation parameters, run: + + ovhcloud cloud loadbalancer create --from-file ./params.json + + Note that you can also pipe the content of the parameters file. + +2. Using your default text editor: + + ovhcloud cloud loadbalancer create --editor + +3. Using only CLI flags: + + ovhcloud cloud loadbalancer create --name my-lb --flavor +`, + Run: cloud.CreateCloudLoadbalancer, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.Name, "name", "", "Name of the loadbalancer") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.FlavorId, "flavor", "", "Flavor ID (can be retrieved with 'cloud reference loadbalancer list-flavors ')") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.Network.Private.Network.Id, "network-id", "", "Network ID") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.Network.Private.Network.SubnetId, "subnet-id", "", "Subnet ID") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.Network.Private.FloatingIp.Id, "floating-ip", "", "Floating IP ID to associate to the loadbalancer") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.Network.Private.Gateway.Id, "gateway", "", "Gateway ID to associate to the loadbalancer") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer", "post", cloud.LoadbalancerCreationExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +func getLoadbalancerAssociateFloatingIpCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "associate-floating-ip ", + Short: "Associate an existing floating IP to a loadbalancer", + Run: cloud.AssociateFloatingIpToLoadbalancer, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().StringVar(&cloud.CloudLoadbalancerAssociateFloatingIpSpec.FloatingIpId, "floating-ip-id", "", "Floating IP ID") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerAssociateFloatingIpSpec.Ip, "ip", "", "Private loadbalancer IP to associate the floating IP with") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer/{loadBalancerId}/associateFloatingIp", "post", cloud.LoadbalancerAssociateFloatingIpExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +func getLoadbalancerCreateFloatingIpCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create-floating-ip ", + Short: "Create a floating IP and attach it to a loadbalancer", + Run: cloud.CreateFloatingIpForLoadbalancer, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateFloatingIpSpec.Ip, "ip", "", "Private loadbalancer IP to associate the floating IP with") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer/{loadBalancerId}/floatingIp", "post", cloud.LoadbalancerCreateFloatingIpExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +// Listener sub-commands + +func initListenerSubCommands(loadbalancerCmd *cobra.Command) { + listenerCmd := &cobra.Command{ + Use: "listener", + Short: "Manage listeners of loadbalancers", + } + loadbalancerCmd.AddCommand(listenerCmd) + + listenerCmd.AddCommand(withFilterFlag(&cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "List all listeners", + Run: cloud.ListCloudLoadbalancerListeners, + })) + + listenerCmd.AddCommand(&cobra.Command{ + Use: "get ", + Short: "Get a specific listener", + Run: cloud.GetCloudLoadbalancerListener, + Args: cobra.ExactArgs(1), + }) + + listenerCmd.AddCommand(getListenerCreationCmd()) + + listenerEditCmd := &cobra.Command{ + Use: "edit ", + Short: "Edit a specific listener", + Run: cloud.EditCloudLoadbalancerListener, + Args: cobra.ExactArgs(1), + } + listenerEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerUpdateSpec.Name, "name", "", "Name of the listener") + listenerEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerUpdateSpec.Description, "description", "", "Description of the listener") + listenerEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerUpdateSpec.DefaultPoolId, "default-pool-id", "", "Default pool ID") + listenerEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerUpdateSpec.CertificateId, "certificate-id", "", "Certificate ID") + addInteractiveEditorFlag(listenerEditCmd) + listenerCmd.AddCommand(listenerEditCmd) + + listenerCmd.AddCommand(&cobra.Command{ + Use: "delete ", + Short: "Delete a specific listener", + Run: cloud.DeleteCloudLoadbalancerListener, + Args: cobra.ExactArgs(1), + }) +} + +func getListenerCreationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create ", + Short: "Create a listener in the given region", + Run: cloud.CreateCloudLoadbalancerListener, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerCreateSpec.LoadbalancerId, "loadbalancer-id", "", "Loadbalancer ID") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerCreateSpec.Name, "name", "", "Name of the listener") + cmd.Flags().IntVar(&cloud.CloudLoadbalancerListenerCreateSpec.Port, "port", 0, "Port to listen on") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerCreateSpec.Protocol, "protocol", "", "Protocol (http, https, tcp, udp, sctp, prometheus)") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/listener", "post", cloud.LoadbalancerListenerCreationExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +// Pool sub-commands + +func initPoolSubCommands(loadbalancerCmd *cobra.Command) { + poolCmd := &cobra.Command{ + Use: "pool", + Short: "Manage pools of loadbalancers", + } + loadbalancerCmd.AddCommand(poolCmd) + + poolCmd.AddCommand(withFilterFlag(&cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "List all pools", + Run: cloud.ListCloudLoadbalancerPools, + })) + + poolCmd.AddCommand(&cobra.Command{ + Use: "get ", + Short: "Get a specific pool", + Run: cloud.GetCloudLoadbalancerPool, + Args: cobra.ExactArgs(1), + }) + + poolCmd.AddCommand(getPoolCreationCmd()) + + poolEditCmd := &cobra.Command{ + Use: "edit ", + Short: "Edit a specific pool", + Run: cloud.EditCloudLoadbalancerPool, + Args: cobra.ExactArgs(1), + } + poolEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolUpdateSpec.Algorithm, "algorithm", "", "Algorithm (roundRobin, leastConnections, sourceIp)") + poolEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolUpdateSpec.Name, "name", "", "Name of the pool") + addInteractiveEditorFlag(poolEditCmd) + poolCmd.AddCommand(poolEditCmd) + + poolCmd.AddCommand(&cobra.Command{ + Use: "delete ", + Short: "Delete a specific pool", + Run: cloud.DeleteCloudLoadbalancerPool, + Args: cobra.ExactArgs(1), + }) + + // Pool Member sub-commands + initPoolMemberSubCommands(poolCmd) +} + +func getPoolCreationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create ", + Short: "Create a pool in the given region", + Run: cloud.CreateCloudLoadbalancerPool, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolCreateSpec.Algorithm, "algorithm", "", "Algorithm (roundRobin, leastConnections, sourceIp)") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolCreateSpec.ListenerId, "listener-id", "", "Listener ID") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolCreateSpec.LoadbalancerId, "loadbalancer-id", "", "Loadbalancer ID") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolCreateSpec.Name, "name", "", "Name of the pool") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolCreateSpec.Protocol, "protocol", "", "Protocol (http, https, tcp, udp, sctp, prometheus)") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/pool", "post", cloud.LoadbalancerPoolCreationExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +func initPoolMemberSubCommands(poolCmd *cobra.Command) { + memberCmd := &cobra.Command{ + Use: "member", + Short: "Manage members of a loadbalancer pool", + } + poolCmd.AddCommand(memberCmd) + + memberCmd.AddCommand(withFilterFlag(&cobra.Command{ + Use: "list ", + Aliases: []string{"ls"}, + Short: "List members of a specific pool", + Run: cloud.ListCloudLoadbalancerPoolMembers, + Args: cobra.ExactArgs(1), + })) + + memberCmd.AddCommand(&cobra.Command{ + Use: "get ", + Short: "Get a specific pool member", + Run: cloud.GetCloudLoadbalancerPoolMember, + Args: cobra.ExactArgs(2), + }) + + memberCmd.AddCommand(getPoolMemberCreationCmd()) + + memberEditCmd := &cobra.Command{ + Use: "edit ", + Short: "Edit a specific pool member", + Run: cloud.EditCloudLoadbalancerPoolMember, + Args: cobra.ExactArgs(2), + } + memberEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolMemberUpdateSpec.Name, "name", "", "Name of the member") + memberEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerPoolMemberUpdateSpec.Weight, "weight", 0, "Weight of the member (1-256)") + addInteractiveEditorFlag(memberEditCmd) + memberCmd.AddCommand(memberEditCmd) + + memberCmd.AddCommand(&cobra.Command{ + Use: "delete ", + Short: "Delete a specific pool member", + Run: cloud.DeleteCloudLoadbalancerPoolMember, + Args: cobra.ExactArgs(2), + }) +} + +func getPoolMemberCreationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create ", + Short: "Create member(s) in a specific pool", + Run: cloud.CreateCloudLoadbalancerPoolMember, + Args: cobra.ExactArgs(1), + } + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/pool/{poolId}/member", "post", cloud.LoadbalancerPoolMemberCreationExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +// Health Monitor sub-commands + +func initHealthMonitorSubCommands(loadbalancerCmd *cobra.Command) { + healthMonitorCmd := &cobra.Command{ + Use: "health-monitor", + Short: "Manage health monitors of loadbalancers", + } + loadbalancerCmd.AddCommand(healthMonitorCmd) + + healthMonitorCmd.AddCommand(withFilterFlag(&cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "List all health monitors", + Run: cloud.ListCloudLoadbalancerHealthMonitors, + })) + + healthMonitorCmd.AddCommand(&cobra.Command{ + Use: "get ", + Short: "Get a specific health monitor", + Run: cloud.GetCloudLoadbalancerHealthMonitor, + Args: cobra.ExactArgs(1), + }) + + healthMonitorCmd.AddCommand(getHealthMonitorCreationCmd()) + + healthMonitorEditCmd := &cobra.Command{ + Use: "edit ", + Short: "Edit a specific health monitor", + Run: cloud.EditCloudLoadbalancerHealthMonitor, + Args: cobra.ExactArgs(1), + } + healthMonitorEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorUpdateSpec.Delay, "delay", 0, "Duration between sending probes to members, in seconds") + healthMonitorEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorUpdateSpec.MaxRetries, "max-retries", 0, "Number of successful checks before changing status to ONLINE") + healthMonitorEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorUpdateSpec.MaxRetriesDown, "max-retries-down", 0, "Number of allowed check failures before changing status to ERROR") + healthMonitorEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerHealthMonitorUpdateSpec.Name, "name", "", "Name of the health monitor") + healthMonitorEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorUpdateSpec.Timeout, "timeout", 0, "Maximum time in seconds to connect before timeout") + addInteractiveEditorFlag(healthMonitorEditCmd) + healthMonitorCmd.AddCommand(healthMonitorEditCmd) + + healthMonitorCmd.AddCommand(&cobra.Command{ + Use: "delete ", + Short: "Delete a specific health monitor", + Run: cloud.DeleteCloudLoadbalancerHealthMonitor, + Args: cobra.ExactArgs(1), + }) +} + +func getHealthMonitorCreationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create ", + Short: "Create a health monitor in the given region", + Run: cloud.CreateCloudLoadbalancerHealthMonitor, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.Delay, "delay", 0, "Duration between sending probes to members, in seconds") + cmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.MaxRetries, "max-retries", 0, "Number of successful checks before changing status to ONLINE") + cmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.MaxRetriesDown, "max-retries-down", 0, "Number of allowed check failures before changing status to ERROR") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.MonitorType, "monitor-type", "", "Type of the monitor (http, https, ping, tcp, tls-hello, udp-connect, sctp)") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.Name, "name", "", "Name of the health monitor") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.PoolId, "pool-id", "", "Pool ID") + cmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.Timeout, "timeout", 0, "Maximum time in seconds to connect before timeout") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/healthMonitor", "post", cloud.LoadbalancerHealthMonitorCreationExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +// L7 Policy sub-commands + +func initL7PolicySubCommands(loadbalancerCmd *cobra.Command) { + l7PolicyCmd := &cobra.Command{ + Use: "l7policy", + Short: "Manage L7 policies of loadbalancers", + } + loadbalancerCmd.AddCommand(l7PolicyCmd) + + l7PolicyCmd.AddCommand(withFilterFlag(&cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "List all L7 policies", + Run: cloud.ListCloudLoadbalancerL7Policies, + })) + + l7PolicyCmd.AddCommand(&cobra.Command{ + Use: "get ", + Short: "Get a specific L7 policy", + Run: cloud.GetCloudLoadbalancerL7Policy, + Args: cobra.ExactArgs(1), + }) + + l7PolicyCmd.AddCommand(getL7PolicyCreationCmd()) + + l7PolicyEditCmd := &cobra.Command{ + Use: "edit ", + Short: "Edit a specific L7 policy", + Run: cloud.EditCloudLoadbalancerL7Policy, + Args: cobra.ExactArgs(1), + } + l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.Action, "action", "", "L7 policy action") + l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.Description, "description", "", "Description") + l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.ListenerId, "listener-id", "", "Listener ID") + l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.Name, "name", "", "Name of the L7 policy") + l7PolicyEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.Position, "position", 0, "Position on the listener") + l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.RedirectPoolId, "redirect-pool-id", "", "Redirect pool ID") + l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.RedirectPrefix, "redirect-prefix", "", "Redirect prefix URL") + l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.RedirectUrl, "redirect-url", "", "Redirect URL") + addInteractiveEditorFlag(l7PolicyEditCmd) + l7PolicyCmd.AddCommand(l7PolicyEditCmd) + + l7PolicyCmd.AddCommand(&cobra.Command{ + Use: "delete ", + Short: "Delete a specific L7 policy", + Run: cloud.DeleteCloudLoadbalancerL7Policy, + Args: cobra.ExactArgs(1), + }) + + // L7 Rule sub-commands + initL7RuleSubCommands(l7PolicyCmd) +} + +func getL7PolicyCreationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create ", + Short: "Create an L7 policy in the given region", + Run: cloud.CreateCloudLoadbalancerL7Policy, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.Action, "action", "", "L7 policy action (redirectToPool, redirectToUrl, redirectPrefix, reject)") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.Description, "description", "", "Description") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.ListenerId, "listener-id", "", "Listener ID") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.Name, "name", "", "Name of the L7 policy") + cmd.Flags().IntVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.Position, "position", 0, "Position on the listener") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.RedirectPoolId, "redirect-pool-id", "", "Redirect pool ID") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.RedirectPrefix, "redirect-prefix", "", "Redirect prefix URL") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.RedirectUrl, "redirect-url", "", "Redirect URL") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/l7Policy", "post", cloud.LoadbalancerL7PolicyCreationExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +func initL7RuleSubCommands(l7PolicyCmd *cobra.Command) { + l7RuleCmd := &cobra.Command{ + Use: "l7rule", + Short: "Manage L7 rules of a loadbalancer L7 policy", + } + l7PolicyCmd.AddCommand(l7RuleCmd) + + l7RuleCmd.AddCommand(withFilterFlag(&cobra.Command{ + Use: "list ", + Aliases: []string{"ls"}, + Short: "List L7 rules of a specific L7 policy", + Run: cloud.ListCloudLoadbalancerL7Rules, + Args: cobra.ExactArgs(1), + })) + + l7RuleCmd.AddCommand(&cobra.Command{ + Use: "get ", + Short: "Get a specific L7 rule", + Run: cloud.GetCloudLoadbalancerL7Rule, + Args: cobra.ExactArgs(2), + }) + + l7RuleCmd.AddCommand(getL7RuleCreationCmd()) + + l7RuleEditCmd := &cobra.Command{ + Use: "edit ", + Short: "Edit a specific L7 rule", + Run: cloud.EditCloudLoadbalancerL7Rule, + Args: cobra.ExactArgs(2), + } + l7RuleEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleUpdateSpec.CompareType, "compare-type", "", "Comparison type") + l7RuleEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleUpdateSpec.Key, "key", "", "Key to use for comparison") + l7RuleEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleUpdateSpec.RuleType, "rule-type", "", "Rule type") + l7RuleEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleUpdateSpec.Value, "value", "", "Value to compare") + addInteractiveEditorFlag(l7RuleEditCmd) + l7RuleCmd.AddCommand(l7RuleEditCmd) + + l7RuleCmd.AddCommand(&cobra.Command{ + Use: "delete ", + Short: "Delete a specific L7 rule", + Run: cloud.DeleteCloudLoadbalancerL7Rule, + Args: cobra.ExactArgs(2), + }) +} + +func getL7RuleCreationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create ", + Short: "Create an L7 rule in a specific L7 policy", + Run: cloud.CreateCloudLoadbalancerL7Rule, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleCreateSpec.CompareType, "compare-type", "", "Comparison type (contains, endsWith, equalTo, regex, startsWith)") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleCreateSpec.Key, "key", "", "Key to use for comparison") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleCreateSpec.RuleType, "rule-type", "", "Rule type (cookie, fileType, header, hostName, path, sslConnHasCert, sslDNField, sslVerifyResult)") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleCreateSpec.Value, "value", "", "Value to compare") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/l7Policy/{l7PolicyId}/l7Rule", "post", cloud.LoadbalancerL7RuleCreationExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} + +// Log sub-commands + +func initLogSubCommands(loadbalancerCmd *cobra.Command) { + logCmd := &cobra.Command{ + Use: "log", + Short: "Manage loadbalancer logs", + } + loadbalancerCmd.AddCommand(logCmd) + + logCmd.AddCommand(withFilterFlag(&cobra.Command{ + Use: "list-kinds ", + Short: "List available log kinds", + Run: cloud.ListCloudLoadbalancerLogKinds, + Args: cobra.ExactArgs(1), + })) + + logCmd.AddCommand(&cobra.Command{ + Use: "get-kind ", + Short: "Get a specific log kind", + Run: cloud.GetCloudLoadbalancerLogKind, + Args: cobra.ExactArgs(2), + }) + + logURLCmd := &cobra.Command{ + Use: "generate-url ", + Short: "Generate a temporary URL to retrieve logs", + Run: cloud.GenerateCloudLoadbalancerLogURL, + Args: cobra.ExactArgs(1), + } + logURLCmd.Flags().StringVar(&cloud.CloudLoadbalancerLogURLSpec.Kind, "kind", "", "Log kind (e.g., haproxy)") + logURLCmd.MarkFlagRequired("kind") + logCmd.AddCommand(logURLCmd) + + // Log Subscription sub-commands + subscriptionCmd := &cobra.Command{ + Use: "subscription", + Short: "Manage log subscriptions for a loadbalancer", + } + logCmd.AddCommand(subscriptionCmd) + + subscriptionCmd.AddCommand(withFilterFlag(&cobra.Command{ + Use: "list ", + Aliases: []string{"ls"}, + Short: "List log subscriptions of a loadbalancer", + Run: cloud.ListCloudLoadbalancerLogSubscriptions, + Args: cobra.ExactArgs(1), + })) + + subscriptionCmd.AddCommand(&cobra.Command{ + Use: "get ", + Short: "Get a specific log subscription", + Run: cloud.GetCloudLoadbalancerLogSubscription, + Args: cobra.ExactArgs(2), + }) + + subscriptionCmd.AddCommand(getLogSubscriptionCreationCmd()) + + subscriptionCmd.AddCommand(&cobra.Command{ + Use: "delete ", + Short: "Delete a log subscription", + Run: cloud.DeleteCloudLoadbalancerLogSubscription, + Args: cobra.ExactArgs(2), + }) +} + +func getLogSubscriptionCreationCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create ", + Short: "Create a log subscription for a loadbalancer", + Run: cloud.CreateCloudLoadbalancerLogSubscription, + Args: cobra.ExactArgs(1), + } + + cmd.Flags().StringVar(&cloud.CloudLoadbalancerLogSubscriptionCreateSpec.Kind, "kind", "", "Log kind (e.g., haproxy)") + cmd.Flags().StringVar(&cloud.CloudLoadbalancerLogSubscriptionCreateSpec.StreamId, "stream-id", "", "LDP stream ID") + + addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer/{loadBalancerId}/log/subscription", "post", cloud.LoadbalancerLogSubscriptionCreationExample, nil) + addInteractiveEditorFlag(cmd) + cmd.MarkFlagsMutuallyExclusive("from-file", "editor") + + return cmd +} diff --git a/internal/cmd/cloud_network_loadbalancer_test.go b/internal/cmd/cloud_loadbalancer_test.go similarity index 81% rename from internal/cmd/cloud_network_loadbalancer_test.go rename to internal/cmd/cloud_loadbalancer_test.go index 02d5ae03..8f468a4d 100644 --- a/internal/cmd/cloud_network_loadbalancer_test.go +++ b/internal/cmd/cloud_loadbalancer_test.go @@ -53,6 +53,114 @@ func registerLoadbalancingRegionMocks() { }`)) } +// --------------------------------------------------------------------------- +// Loadbalancer – get +// --------------------------------------------------------------------------- + +func (ms *MockSuite) TestCloudLoadbalancerGetCmd(assert, require *td.T) { + httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region", + httpmock.NewStringResponder(200, `["GRA11", "SBG5", "BHS5"]`)) + + httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11", + httpmock.NewStringResponder(200, `{ + "name": "GRA11", + "type": "region", + "status": "UP", + "services": [ + { + "name": "octavialoadbalancer", + "status": "UP" + } + ], + "countryCode": "fr", + "ipCountries": [], + "continentCode": "NA", + "availabilityZones": [], + "datacenterLocation": "GRA11" + }`)) + + httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5", + httpmock.NewStringResponder(200, `{ + "name": "SBG5", + "type": "region", + "status": "UP", + "services": [ + { + "name": "octavialoadbalancer", + "status": "UP" + } + ], + "countryCode": "fr", + "ipCountries": [], + "continentCode": "NA", + "availabilityZones": [], + "datacenterLocation": "SBG5" + }`)) + + httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/BHS5", + httpmock.NewStringResponder(200, `{ + "name": "BHS5", + "type": "region", + "status": "UP", + "services": [], + "countryCode": "ca", + "ipCountries": [], + "continentCode": "NA", + "availabilityZones": [], + "datacenterLocation": "BHS5" + }`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/loadbalancer/fakeLB", + httpmock.NewStringResponder(200, `{ + "createdAt": "2024-07-30T08:26:51Z", + "flavorId": "f862fa22-6275-4f8f-885e-66a8faf5e44e", + "floatingIp": null, + "id": "334fc97e-a8db-11f0-944d-0050568ce122", + "name": "loadbalancer-sbg5-2024-07-30", + "operatingStatus": "online", + "provisioningStatus": "active", + "region": "SBG5", + "updatedAt": "2025-10-14T08:48:33Z", + "vipAddress": "1.2.3.4", + "vipNetworkId": "3f29f530-a8db-11f0-9ab2-0050568ce122", + "vipSubnetId": "44a869c4-a8db-11f0-899f-0050568ce122" + }`)) + + httpmock.RegisterResponder(http.MethodGet, + "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/flavor/f862fa22-6275-4f8f-885e-66a8faf5e44e", + httpmock.NewStringResponder(200, `{ + "id": "f862fa22-6275-4f8f-885e-66a8faf5e44e", + "name": "medium", + "description": "Medium Load Balancer Flavor" + }`)) + + out, err := cmd.Execute("cloud", "loadbalancer", "get", "fakeLB", "--cloud-project", "fakeProjectID") + require.CmpNoError(err) + assert.Cmp(cleanWhitespacesHelper(out), ` + # 🚀 Load balancer fakeLB + + *loadbalancer-sbg5-2024-07-30* + + ## General information + + **Region**: SBG5 + **Operating status**: online + **Provisioning status**: active + **Flavor**: medium (ID: f862fa22-6275-4f8f-885e-66a8faf5e44e) + **Creation date**: 2024-07-30T08:26:51Z + + ## Technical information + + **VIP address**: 1.2.3.4 + **VIP network ID**: 3f29f530-a8db-11f0-9ab2-0050568ce122 + **VIP subnet ID**: 44a869c4-a8db-11f0-899f-0050568ce122 + + 💡 Use option -o json or -o yaml to get the raw output with all information + +`) +} + // --------------------------------------------------------------------------- // Loadbalancer – list // --------------------------------------------------------------------------- @@ -84,7 +192,7 @@ func (ms *MockSuite) TestCloudLoadbalancerListCmd(assert, require *td.T) { } ]`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "ls", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "ls", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`[ { @@ -124,7 +232,7 @@ func (ms *MockSuite) TestCloudLoadbalancerDeleteCmd(assert, require *td.T) { "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/loadbalancer/lb-sbg-001", httpmock.NewStringResponder(200, `null`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "delete", "lb-sbg-001", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "delete", "lb-sbg-001", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`{ "message": "✅ Loadbalancer lb-sbg-001 deleted successfully" @@ -152,7 +260,7 @@ func (ms *MockSuite) TestCloudLoadbalancerStatsCmd(assert, require *td.T) { "totalConnections": 500 }`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "stats", "lb-gra-001", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "stats", "lb-gra-001", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`{ "activeConnections": 42, @@ -187,7 +295,7 @@ func (ms *MockSuite) TestCloudLoadbalancerListenerListCmd(assert, require *td.T) "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/listener", httpmock.NewStringResponder(200, `[]`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "listener", "ls", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "listener", "ls", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`[ { @@ -225,7 +333,7 @@ func (ms *MockSuite) TestCloudLoadbalancerListenerGetCmd(assert, require *td.T) "description": "My HTTP listener" }`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "listener", "get", "lis-001", "--cloud-project", "fakeProjectID") + out, err := cmd.Execute("cloud", "loadbalancer", "listener", "get", "lis-001", "--cloud-project", "fakeProjectID") require.CmpNoError(err) assert.Cmp(cleanWhitespacesHelper(out), ` # 🔊 Listener lis-001 @@ -261,7 +369,7 @@ func (ms *MockSuite) TestCloudLoadbalancerListenerDeleteCmd(assert, require *td. "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/listener/lis-001", httpmock.NewStringResponder(200, `null`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "listener", "delete", "lis-001", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "listener", "delete", "lis-001", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`{ "message": "✅ Listener lis-001 deleted successfully" @@ -292,7 +400,7 @@ func (ms *MockSuite) TestCloudLoadbalancerPoolListCmd(assert, require *td.T) { "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/pool", httpmock.NewStringResponder(200, `[]`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "pool", "ls", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "pool", "ls", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`[ { @@ -326,7 +434,7 @@ func (ms *MockSuite) TestCloudLoadbalancerPoolGetCmd(assert, require *td.T) { "listenerId": "lis-001" }`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "pool", "get", "pool-001", "--cloud-project", "fakeProjectID") + out, err := cmd.Execute("cloud", "loadbalancer", "pool", "get", "pool-001", "--cloud-project", "fakeProjectID") require.CmpNoError(err) assert.Cmp(cleanWhitespacesHelper(out), ` # 🏊 Pool pool-001 @@ -362,7 +470,7 @@ func (ms *MockSuite) TestCloudLoadbalancerPoolDeleteCmd(assert, require *td.T) { "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/pool/pool-001", httpmock.NewStringResponder(200, `null`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "pool", "delete", "pool-001", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "pool", "delete", "pool-001", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`{ "message": "✅ Pool pool-001 deleted successfully" @@ -402,7 +510,7 @@ func (ms *MockSuite) TestCloudLoadbalancerPoolMemberListCmd(assert, require *td. } ]`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "pool", "member", "ls", "pool-001", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "pool", "member", "ls", "pool-001", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`[ { @@ -447,7 +555,7 @@ func (ms *MockSuite) TestCloudLoadbalancerPoolMemberGetCmd(assert, require *td.T "provisioningStatus": "active" }`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "pool", "member", "get", "pool-001", "mem-001", "--cloud-project", "fakeProjectID") + out, err := cmd.Execute("cloud", "loadbalancer", "pool", "member", "get", "pool-001", "mem-001", "--cloud-project", "fakeProjectID") require.CmpNoError(err) assert.Cmp(cleanWhitespacesHelper(out), ` # 👤 Pool member mem-001 @@ -482,7 +590,7 @@ func (ms *MockSuite) TestCloudLoadbalancerPoolMemberDeleteCmd(assert, require *t "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/pool/pool-001/member/mem-001", httpmock.NewStringResponder(200, `null`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "pool", "member", "delete", "pool-001", "mem-001", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "pool", "member", "delete", "pool-001", "mem-001", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`{ "message": "✅ Pool member mem-001 deleted successfully" @@ -513,7 +621,7 @@ func (ms *MockSuite) TestCloudLoadbalancerHealthMonitorListCmd(assert, require * "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/healthMonitor", httpmock.NewStringResponder(200, `[]`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "health-monitor", "ls", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "health-monitor", "ls", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`[ { @@ -549,7 +657,7 @@ func (ms *MockSuite) TestCloudLoadbalancerHealthMonitorGetCmd(assert, require *t "provisioningStatus": "active" }`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "health-monitor", "get", "hm-001", "--cloud-project", "fakeProjectID") + out, err := cmd.Execute("cloud", "loadbalancer", "health-monitor", "get", "hm-001", "--cloud-project", "fakeProjectID") require.CmpNoError(err) assert.Cmp(cleanWhitespacesHelper(out), ` # 💓 Health monitor hm-001 @@ -587,7 +695,7 @@ func (ms *MockSuite) TestCloudLoadbalancerHealthMonitorDeleteCmd(assert, require "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/healthMonitor/hm-001", httpmock.NewStringResponder(200, `null`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "health-monitor", "delete", "hm-001", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "health-monitor", "delete", "hm-001", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`{ "message": "✅ Health monitor hm-001 deleted successfully" @@ -618,7 +726,7 @@ func (ms *MockSuite) TestCloudLoadbalancerL7PolicyListCmd(assert, require *td.T) "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/l7Policy", httpmock.NewStringResponder(200, `[]`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "l7policy", "ls", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "l7policy", "ls", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`[ { @@ -655,7 +763,7 @@ func (ms *MockSuite) TestCloudLoadbalancerL7PolicyGetCmd(assert, require *td.T) "redirectPrefix": "" }`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "l7policy", "get", "l7p-001", "--cloud-project", "fakeProjectID") + out, err := cmd.Execute("cloud", "loadbalancer", "l7policy", "get", "l7p-001", "--cloud-project", "fakeProjectID") require.CmpNoError(err) assert.Cmp(cleanWhitespacesHelper(out), ` # 📋 L7 policy l7p-001 @@ -692,7 +800,7 @@ func (ms *MockSuite) TestCloudLoadbalancerL7PolicyDeleteCmd(assert, require *td. "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/l7Policy/l7p-001", httpmock.NewStringResponder(200, `null`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "l7policy", "delete", "l7p-001", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "l7policy", "delete", "l7p-001", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`{ "message": "✅ L7 policy l7p-001 deleted successfully" @@ -724,7 +832,7 @@ func (ms *MockSuite) TestCloudLoadbalancerL7RuleListCmd(assert, require *td.T) { } ]`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "l7policy", "l7rule", "ls", "l7p-001", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "l7policy", "l7rule", "ls", "l7p-001", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`[ { @@ -762,7 +870,7 @@ func (ms *MockSuite) TestCloudLoadbalancerL7RuleGetCmd(assert, require *td.T) { "provisioningStatus": "active" }`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "l7policy", "l7rule", "get", "l7p-001", "l7r-001", "--cloud-project", "fakeProjectID") + out, err := cmd.Execute("cloud", "loadbalancer", "l7policy", "l7rule", "get", "l7p-001", "l7r-001", "--cloud-project", "fakeProjectID") require.CmpNoError(err) assert.Cmp(cleanWhitespacesHelper(out), ` # 📏 L7 rule l7r-001 @@ -797,7 +905,7 @@ func (ms *MockSuite) TestCloudLoadbalancerL7RuleDeleteCmd(assert, require *td.T) "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/l7Policy/l7p-001/l7Rule/l7r-001", httpmock.NewStringResponder(200, `null`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "l7policy", "l7rule", "delete", "l7p-001", "l7r-001", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "l7policy", "l7rule", "delete", "l7p-001", "l7r-001", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`{ "message": "✅ L7 rule l7r-001 deleted successfully" @@ -813,7 +921,7 @@ func (ms *MockSuite) TestCloudLoadbalancerLogKindListCmd(assert, require *td.T) "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/log/kind", httpmock.NewStringResponder(200, `["haproxy"]`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "log", "list-kinds", "GRA11", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "log", "list-kinds", "GRA11", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`{ "kinds": [ @@ -835,7 +943,7 @@ func (ms *MockSuite) TestCloudLoadbalancerLogKindGetCmd(assert, require *td.T) { "displayName": "HAProxy logs" }`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "log", "get-kind", "GRA11", "haproxy", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "log", "get-kind", "GRA11", "haproxy", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`{ "name": "haproxy", @@ -861,7 +969,7 @@ func (ms *MockSuite) TestCloudLoadbalancerLogGenerateURLCmd(assert, require *td. "url": "https://logs.example.com/temp/abc123" }`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "log", "generate-url", "lb-gra-001", "--kind", "haproxy", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "log", "generate-url", "lb-gra-001", "--kind", "haproxy", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`{ "message": "✅ Temporary log URL generated successfully: https://logs.example.com/temp/abc123", @@ -893,7 +1001,7 @@ func (ms *MockSuite) TestCloudLoadbalancerLogSubscriptionListCmd(assert, require } ]`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "log", "subscription", "ls", "lb-gra-001", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "log", "subscription", "ls", "lb-gra-001", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`[ { @@ -927,7 +1035,7 @@ func (ms *MockSuite) TestCloudLoadbalancerLogSubscriptionGetCmd(assert, require "updatedAt": "2024-01-15T10:30:00Z" }`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "log", "subscription", "get", "lb-gra-001", "sub-001", "--cloud-project", "fakeProjectID") + out, err := cmd.Execute("cloud", "loadbalancer", "log", "subscription", "get", "lb-gra-001", "sub-001", "--cloud-project", "fakeProjectID") require.CmpNoError(err) assert.Cmp(cleanWhitespacesHelper(out), ` # 📝 Log subscription sub-001 @@ -961,7 +1069,7 @@ func (ms *MockSuite) TestCloudLoadbalancerLogSubscriptionDeleteCmd(assert, requi "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11/loadbalancing/loadbalancer/lb-gra-001/log/subscription/sub-001", httpmock.NewStringResponder(200, `null`)) - out, err := cmd.Execute("cloud", "network", "loadbalancer", "log", "subscription", "delete", "lb-gra-001", "sub-001", "--cloud-project", "fakeProjectID", "--json") + out, err := cmd.Execute("cloud", "loadbalancer", "log", "subscription", "delete", "lb-gra-001", "sub-001", "--cloud-project", "fakeProjectID", "-o", "json") require.CmpNoError(err) assert.Cmp(json.RawMessage(out), td.JSON(`{ "message": "✅ Log subscription sub-001 deleted successfully" diff --git a/internal/cmd/cloud_network_loadbalancer.go b/internal/cmd/cloud_network_loadbalancer.go deleted file mode 100644 index c7b7194c..00000000 --- a/internal/cmd/cloud_network_loadbalancer.go +++ /dev/null @@ -1,622 +0,0 @@ -// SPDX-FileCopyrightText: 2025 OVH SAS -// -// SPDX-License-Identifier: Apache-2.0 - -package cmd - -import ( - "github.com/ovh/ovhcloud-cli/internal/assets" - "github.com/ovh/ovhcloud-cli/internal/services/cloud" - "github.com/spf13/cobra" -) - -func initLoadbalancerSubCommands(loadbalancerCmd *cobra.Command) { - // Loadbalancer create - loadbalancerCmd.AddCommand(getLoadbalancerCreationCmd()) - - // Loadbalancer delete - loadbalancerCmd.AddCommand(&cobra.Command{ - Use: "delete ", - Short: "Delete a specific loadbalancer", - Run: cloud.DeleteCloudLoadbalancer, - Args: cobra.ExactArgs(1), - }) - - // Loadbalancer stats - loadbalancerCmd.AddCommand(&cobra.Command{ - Use: "stats ", - Short: "Get statistics for a loadbalancer", - Run: cloud.GetCloudLoadbalancerStats, - Args: cobra.ExactArgs(1), - }) - - // Associate floating IP - loadbalancerCmd.AddCommand(getLoadbalancerAssociateFloatingIpCmd()) - - // Create floating IP - loadbalancerCmd.AddCommand(getLoadbalancerCreateFloatingIpCmd()) - - // Listener sub-commands - initListenerSubCommands(loadbalancerCmd) - - // Pool sub-commands - initPoolSubCommands(loadbalancerCmd) - - // Health Monitor sub-commands - initHealthMonitorSubCommands(loadbalancerCmd) - - // L7 Policy sub-commands - initL7PolicySubCommands(loadbalancerCmd) - - // Log sub-commands - initLogSubCommands(loadbalancerCmd) -} - -// Loadbalancer creation command helpers - -func getLoadbalancerCreationCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "create ", - Short: "Create a loadbalancer in the given cloud project", - Long: `Use this command to create a loadbalancer. -There are three ways to define the parameters: - -1. Using a configuration file: - - First you can generate an example of parameters file using the following command: - - ovhcloud cloud network loadbalancer create --init-file ./params.json - - After editing the file to set the correct creation parameters, run: - - ovhcloud cloud network loadbalancer create --from-file ./params.json - - Note that you can also pipe the content of the parameters file. - -2. Using your default text editor: - - ovhcloud cloud network loadbalancer create --editor - -3. Using only CLI flags: - - ovhcloud cloud network loadbalancer create --name my-lb --flavor -`, - Run: cloud.CreateCloudLoadbalancer, - Args: cobra.ExactArgs(1), - } - - cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.Name, "name", "", "Name of the loadbalancer") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.FlavorId, "flavor", "", "Flavor ID (can be retrieved with 'cloud reference loadbalancer list-flavors ')") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.Network.Private.Network.Id, "network-id", "", "Network ID") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.Network.Private.Network.SubnetId, "subnet-id", "", "Subnet ID") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.Network.Private.FloatingIp.Id, "floating-ip", "", "Floating IP ID to associate to the loadbalancer") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateSpec.Network.Private.Gateway.Id, "gateway", "", "Gateway ID to associate to the loadbalancer") - - addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer", "post", cloud.LoadbalancerCreationExample, nil) - addInteractiveEditorFlag(cmd) - cmd.MarkFlagsMutuallyExclusive("from-file", "editor") - - return cmd -} - -func getLoadbalancerAssociateFloatingIpCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "associate-floating-ip ", - Short: "Associate an existing floating IP to a loadbalancer", - Run: cloud.AssociateFloatingIpToLoadbalancer, - Args: cobra.ExactArgs(1), - } - - cmd.Flags().StringVar(&cloud.CloudLoadbalancerAssociateFloatingIpSpec.FloatingIpId, "floating-ip-id", "", "Floating IP ID") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerAssociateFloatingIpSpec.Ip, "ip", "", "Private loadbalancer IP to associate the floating IP with") - - addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer/{loadBalancerId}/associateFloatingIp", "post", cloud.LoadbalancerAssociateFloatingIpExample, nil) - addInteractiveEditorFlag(cmd) - cmd.MarkFlagsMutuallyExclusive("from-file", "editor") - - return cmd -} - -func getLoadbalancerCreateFloatingIpCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "create-floating-ip ", - Short: "Create a floating IP and attach it to a loadbalancer", - Run: cloud.CreateFloatingIpForLoadbalancer, - Args: cobra.ExactArgs(1), - } - - cmd.Flags().StringVar(&cloud.CloudLoadbalancerCreateFloatingIpSpec.Ip, "ip", "", "Private loadbalancer IP to associate the floating IP with") - - addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer/{loadBalancerId}/floatingIp", "post", cloud.LoadbalancerCreateFloatingIpExample, nil) - addInteractiveEditorFlag(cmd) - cmd.MarkFlagsMutuallyExclusive("from-file", "editor") - - return cmd -} - -// Listener sub-commands - -func initListenerSubCommands(loadbalancerCmd *cobra.Command) { - listenerCmd := &cobra.Command{ - Use: "listener", - Short: "Manage listeners of loadbalancers", - } - loadbalancerCmd.AddCommand(listenerCmd) - - listenerCmd.AddCommand(withFilterFlag(&cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "List all listeners", - Run: cloud.ListCloudLoadbalancerListeners, - })) - - listenerCmd.AddCommand(&cobra.Command{ - Use: "get ", - Short: "Get a specific listener", - Run: cloud.GetCloudLoadbalancerListener, - Args: cobra.ExactArgs(1), - }) - - listenerCmd.AddCommand(getListenerCreationCmd()) - - listenerEditCmd := &cobra.Command{ - Use: "edit ", - Short: "Edit a specific listener", - Run: cloud.EditCloudLoadbalancerListener, - Args: cobra.ExactArgs(1), - } - listenerEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerUpdateSpec.Name, "name", "", "Name of the listener") - listenerEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerUpdateSpec.Description, "description", "", "Description of the listener") - listenerEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerUpdateSpec.DefaultPoolId, "default-pool-id", "", "Default pool ID") - listenerEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerUpdateSpec.CertificateId, "certificate-id", "", "Certificate ID") - addInteractiveEditorFlag(listenerEditCmd) - listenerCmd.AddCommand(listenerEditCmd) - - listenerCmd.AddCommand(&cobra.Command{ - Use: "delete ", - Short: "Delete a specific listener", - Run: cloud.DeleteCloudLoadbalancerListener, - Args: cobra.ExactArgs(1), - }) -} - -func getListenerCreationCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "create ", - Short: "Create a listener in the given region", - Run: cloud.CreateCloudLoadbalancerListener, - Args: cobra.ExactArgs(1), - } - - cmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerCreateSpec.LoadbalancerId, "loadbalancer-id", "", "Loadbalancer ID") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerCreateSpec.Name, "name", "", "Name of the listener") - cmd.Flags().IntVar(&cloud.CloudLoadbalancerListenerCreateSpec.Port, "port", 0, "Port to listen on") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerListenerCreateSpec.Protocol, "protocol", "", "Protocol (http, https, tcp, udp, sctp, prometheus)") - - addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/listener", "post", cloud.LoadbalancerListenerCreationExample, nil) - addInteractiveEditorFlag(cmd) - cmd.MarkFlagsMutuallyExclusive("from-file", "editor") - - return cmd -} - -// Pool sub-commands - -func initPoolSubCommands(loadbalancerCmd *cobra.Command) { - poolCmd := &cobra.Command{ - Use: "pool", - Short: "Manage pools of loadbalancers", - } - loadbalancerCmd.AddCommand(poolCmd) - - poolCmd.AddCommand(withFilterFlag(&cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "List all pools", - Run: cloud.ListCloudLoadbalancerPools, - })) - - poolCmd.AddCommand(&cobra.Command{ - Use: "get ", - Short: "Get a specific pool", - Run: cloud.GetCloudLoadbalancerPool, - Args: cobra.ExactArgs(1), - }) - - poolCmd.AddCommand(getPoolCreationCmd()) - - poolEditCmd := &cobra.Command{ - Use: "edit ", - Short: "Edit a specific pool", - Run: cloud.EditCloudLoadbalancerPool, - Args: cobra.ExactArgs(1), - } - poolEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolUpdateSpec.Algorithm, "algorithm", "", "Algorithm (roundRobin, leastConnections, sourceIp)") - poolEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolUpdateSpec.Name, "name", "", "Name of the pool") - addInteractiveEditorFlag(poolEditCmd) - poolCmd.AddCommand(poolEditCmd) - - poolCmd.AddCommand(&cobra.Command{ - Use: "delete ", - Short: "Delete a specific pool", - Run: cloud.DeleteCloudLoadbalancerPool, - Args: cobra.ExactArgs(1), - }) - - // Pool Member sub-commands - initPoolMemberSubCommands(poolCmd) -} - -func getPoolCreationCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "create ", - Short: "Create a pool in the given region", - Run: cloud.CreateCloudLoadbalancerPool, - Args: cobra.ExactArgs(1), - } - - cmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolCreateSpec.Algorithm, "algorithm", "", "Algorithm (roundRobin, leastConnections, sourceIp)") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolCreateSpec.ListenerId, "listener-id", "", "Listener ID") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolCreateSpec.LoadbalancerId, "loadbalancer-id", "", "Loadbalancer ID") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolCreateSpec.Name, "name", "", "Name of the pool") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolCreateSpec.Protocol, "protocol", "", "Protocol (http, https, tcp, udp, sctp, prometheus)") - - addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/pool", "post", cloud.LoadbalancerPoolCreationExample, nil) - addInteractiveEditorFlag(cmd) - cmd.MarkFlagsMutuallyExclusive("from-file", "editor") - - return cmd -} - -func initPoolMemberSubCommands(poolCmd *cobra.Command) { - memberCmd := &cobra.Command{ - Use: "member", - Short: "Manage members of a loadbalancer pool", - } - poolCmd.AddCommand(memberCmd) - - memberCmd.AddCommand(withFilterFlag(&cobra.Command{ - Use: "list ", - Aliases: []string{"ls"}, - Short: "List members of a specific pool", - Run: cloud.ListCloudLoadbalancerPoolMembers, - Args: cobra.ExactArgs(1), - })) - - memberCmd.AddCommand(&cobra.Command{ - Use: "get ", - Short: "Get a specific pool member", - Run: cloud.GetCloudLoadbalancerPoolMember, - Args: cobra.ExactArgs(2), - }) - - memberCmd.AddCommand(getPoolMemberCreationCmd()) - - memberEditCmd := &cobra.Command{ - Use: "edit ", - Short: "Edit a specific pool member", - Run: cloud.EditCloudLoadbalancerPoolMember, - Args: cobra.ExactArgs(2), - } - memberEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerPoolMemberUpdateSpec.Name, "name", "", "Name of the member") - memberEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerPoolMemberUpdateSpec.Weight, "weight", 0, "Weight of the member (1-256)") - addInteractiveEditorFlag(memberEditCmd) - memberCmd.AddCommand(memberEditCmd) - - memberCmd.AddCommand(&cobra.Command{ - Use: "delete ", - Short: "Delete a specific pool member", - Run: cloud.DeleteCloudLoadbalancerPoolMember, - Args: cobra.ExactArgs(2), - }) -} - -func getPoolMemberCreationCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "create ", - Short: "Create member(s) in a specific pool", - Run: cloud.CreateCloudLoadbalancerPoolMember, - Args: cobra.ExactArgs(1), - } - - addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/pool/{poolId}/member", "post", cloud.LoadbalancerPoolMemberCreationExample, nil) - addInteractiveEditorFlag(cmd) - cmd.MarkFlagsMutuallyExclusive("from-file", "editor") - - return cmd -} - -// Health Monitor sub-commands - -func initHealthMonitorSubCommands(loadbalancerCmd *cobra.Command) { - healthMonitorCmd := &cobra.Command{ - Use: "health-monitor", - Short: "Manage health monitors of loadbalancers", - } - loadbalancerCmd.AddCommand(healthMonitorCmd) - - healthMonitorCmd.AddCommand(withFilterFlag(&cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "List all health monitors", - Run: cloud.ListCloudLoadbalancerHealthMonitors, - })) - - healthMonitorCmd.AddCommand(&cobra.Command{ - Use: "get ", - Short: "Get a specific health monitor", - Run: cloud.GetCloudLoadbalancerHealthMonitor, - Args: cobra.ExactArgs(1), - }) - - healthMonitorCmd.AddCommand(getHealthMonitorCreationCmd()) - - healthMonitorEditCmd := &cobra.Command{ - Use: "edit ", - Short: "Edit a specific health monitor", - Run: cloud.EditCloudLoadbalancerHealthMonitor, - Args: cobra.ExactArgs(1), - } - healthMonitorEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorUpdateSpec.Delay, "delay", 0, "Duration between sending probes to members, in seconds") - healthMonitorEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorUpdateSpec.MaxRetries, "max-retries", 0, "Number of successful checks before changing status to ONLINE") - healthMonitorEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorUpdateSpec.MaxRetriesDown, "max-retries-down", 0, "Number of allowed check failures before changing status to ERROR") - healthMonitorEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerHealthMonitorUpdateSpec.Name, "name", "", "Name of the health monitor") - healthMonitorEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorUpdateSpec.Timeout, "timeout", 0, "Maximum time in seconds to connect before timeout") - addInteractiveEditorFlag(healthMonitorEditCmd) - healthMonitorCmd.AddCommand(healthMonitorEditCmd) - - healthMonitorCmd.AddCommand(&cobra.Command{ - Use: "delete ", - Short: "Delete a specific health monitor", - Run: cloud.DeleteCloudLoadbalancerHealthMonitor, - Args: cobra.ExactArgs(1), - }) -} - -func getHealthMonitorCreationCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "create ", - Short: "Create a health monitor in the given region", - Run: cloud.CreateCloudLoadbalancerHealthMonitor, - Args: cobra.ExactArgs(1), - } - - cmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.Delay, "delay", 0, "Duration between sending probes to members, in seconds") - cmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.MaxRetries, "max-retries", 0, "Number of successful checks before changing status to ONLINE") - cmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.MaxRetriesDown, "max-retries-down", 0, "Number of allowed check failures before changing status to ERROR") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.MonitorType, "monitor-type", "", "Type of the monitor (http, https, ping, tcp, tls-hello, udp-connect, sctp)") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.Name, "name", "", "Name of the health monitor") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.PoolId, "pool-id", "", "Pool ID") - cmd.Flags().IntVar(&cloud.CloudLoadbalancerHealthMonitorCreateSpec.Timeout, "timeout", 0, "Maximum time in seconds to connect before timeout") - - addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/healthMonitor", "post", cloud.LoadbalancerHealthMonitorCreationExample, nil) - addInteractiveEditorFlag(cmd) - cmd.MarkFlagsMutuallyExclusive("from-file", "editor") - - return cmd -} - -// L7 Policy sub-commands - -func initL7PolicySubCommands(loadbalancerCmd *cobra.Command) { - l7PolicyCmd := &cobra.Command{ - Use: "l7policy", - Short: "Manage L7 policies of loadbalancers", - } - loadbalancerCmd.AddCommand(l7PolicyCmd) - - l7PolicyCmd.AddCommand(withFilterFlag(&cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "List all L7 policies", - Run: cloud.ListCloudLoadbalancerL7Policies, - })) - - l7PolicyCmd.AddCommand(&cobra.Command{ - Use: "get ", - Short: "Get a specific L7 policy", - Run: cloud.GetCloudLoadbalancerL7Policy, - Args: cobra.ExactArgs(1), - }) - - l7PolicyCmd.AddCommand(getL7PolicyCreationCmd()) - - l7PolicyEditCmd := &cobra.Command{ - Use: "edit ", - Short: "Edit a specific L7 policy", - Run: cloud.EditCloudLoadbalancerL7Policy, - Args: cobra.ExactArgs(1), - } - l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.Action, "action", "", "L7 policy action") - l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.Description, "description", "", "Description") - l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.ListenerId, "listener-id", "", "Listener ID") - l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.Name, "name", "", "Name of the L7 policy") - l7PolicyEditCmd.Flags().IntVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.Position, "position", 0, "Position on the listener") - l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.RedirectPoolId, "redirect-pool-id", "", "Redirect pool ID") - l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.RedirectPrefix, "redirect-prefix", "", "Redirect prefix URL") - l7PolicyEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyUpdateSpec.RedirectUrl, "redirect-url", "", "Redirect URL") - addInteractiveEditorFlag(l7PolicyEditCmd) - l7PolicyCmd.AddCommand(l7PolicyEditCmd) - - l7PolicyCmd.AddCommand(&cobra.Command{ - Use: "delete ", - Short: "Delete a specific L7 policy", - Run: cloud.DeleteCloudLoadbalancerL7Policy, - Args: cobra.ExactArgs(1), - }) - - // L7 Rule sub-commands - initL7RuleSubCommands(l7PolicyCmd) -} - -func getL7PolicyCreationCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "create ", - Short: "Create an L7 policy in the given region", - Run: cloud.CreateCloudLoadbalancerL7Policy, - Args: cobra.ExactArgs(1), - } - - cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.Action, "action", "", "L7 policy action (redirectToPool, redirectToUrl, redirectPrefix, reject)") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.Description, "description", "", "Description") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.ListenerId, "listener-id", "", "Listener ID") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.Name, "name", "", "Name of the L7 policy") - cmd.Flags().IntVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.Position, "position", 0, "Position on the listener") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.RedirectPoolId, "redirect-pool-id", "", "Redirect pool ID") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.RedirectPrefix, "redirect-prefix", "", "Redirect prefix URL") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7PolicyCreateSpec.RedirectUrl, "redirect-url", "", "Redirect URL") - - addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/l7Policy", "post", cloud.LoadbalancerL7PolicyCreationExample, nil) - addInteractiveEditorFlag(cmd) - cmd.MarkFlagsMutuallyExclusive("from-file", "editor") - - return cmd -} - -func initL7RuleSubCommands(l7PolicyCmd *cobra.Command) { - l7RuleCmd := &cobra.Command{ - Use: "l7rule", - Short: "Manage L7 rules of a loadbalancer L7 policy", - } - l7PolicyCmd.AddCommand(l7RuleCmd) - - l7RuleCmd.AddCommand(withFilterFlag(&cobra.Command{ - Use: "list ", - Aliases: []string{"ls"}, - Short: "List L7 rules of a specific L7 policy", - Run: cloud.ListCloudLoadbalancerL7Rules, - Args: cobra.ExactArgs(1), - })) - - l7RuleCmd.AddCommand(&cobra.Command{ - Use: "get ", - Short: "Get a specific L7 rule", - Run: cloud.GetCloudLoadbalancerL7Rule, - Args: cobra.ExactArgs(2), - }) - - l7RuleCmd.AddCommand(getL7RuleCreationCmd()) - - l7RuleEditCmd := &cobra.Command{ - Use: "edit ", - Short: "Edit a specific L7 rule", - Run: cloud.EditCloudLoadbalancerL7Rule, - Args: cobra.ExactArgs(2), - } - l7RuleEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleUpdateSpec.CompareType, "compare-type", "", "Comparison type") - l7RuleEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleUpdateSpec.Key, "key", "", "Key to use for comparison") - l7RuleEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleUpdateSpec.RuleType, "rule-type", "", "Rule type") - l7RuleEditCmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleUpdateSpec.Value, "value", "", "Value to compare") - addInteractiveEditorFlag(l7RuleEditCmd) - l7RuleCmd.AddCommand(l7RuleEditCmd) - - l7RuleCmd.AddCommand(&cobra.Command{ - Use: "delete ", - Short: "Delete a specific L7 rule", - Run: cloud.DeleteCloudLoadbalancerL7Rule, - Args: cobra.ExactArgs(2), - }) -} - -func getL7RuleCreationCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "create ", - Short: "Create an L7 rule in a specific L7 policy", - Run: cloud.CreateCloudLoadbalancerL7Rule, - Args: cobra.ExactArgs(1), - } - - cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleCreateSpec.CompareType, "compare-type", "", "Comparison type (contains, endsWith, equalTo, regex, startsWith)") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleCreateSpec.Key, "key", "", "Key to use for comparison") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleCreateSpec.RuleType, "rule-type", "", "Rule type (cookie, fileType, header, hostName, path, sslConnHasCert, sslDNField, sslVerifyResult)") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerL7RuleCreateSpec.Value, "value", "", "Value to compare") - - addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/l7Policy/{l7PolicyId}/l7Rule", "post", cloud.LoadbalancerL7RuleCreationExample, nil) - addInteractiveEditorFlag(cmd) - cmd.MarkFlagsMutuallyExclusive("from-file", "editor") - - return cmd -} - -// Log sub-commands - -func initLogSubCommands(loadbalancerCmd *cobra.Command) { - logCmd := &cobra.Command{ - Use: "log", - Short: "Manage loadbalancer logs", - } - loadbalancerCmd.AddCommand(logCmd) - - logCmd.AddCommand(withFilterFlag(&cobra.Command{ - Use: "list-kinds ", - Short: "List available log kinds", - Run: cloud.ListCloudLoadbalancerLogKinds, - Args: cobra.ExactArgs(1), - })) - - logCmd.AddCommand(&cobra.Command{ - Use: "get-kind ", - Short: "Get a specific log kind", - Run: cloud.GetCloudLoadbalancerLogKind, - Args: cobra.ExactArgs(2), - }) - - logURLCmd := &cobra.Command{ - Use: "generate-url ", - Short: "Generate a temporary URL to retrieve logs", - Run: cloud.GenerateCloudLoadbalancerLogURL, - Args: cobra.ExactArgs(1), - } - logURLCmd.Flags().StringVar(&cloud.CloudLoadbalancerLogURLSpec.Kind, "kind", "", "Log kind (e.g., haproxy)") - logURLCmd.MarkFlagRequired("kind") - logCmd.AddCommand(logURLCmd) - - // Log Subscription sub-commands - subscriptionCmd := &cobra.Command{ - Use: "subscription", - Short: "Manage log subscriptions for a loadbalancer", - } - logCmd.AddCommand(subscriptionCmd) - - subscriptionCmd.AddCommand(withFilterFlag(&cobra.Command{ - Use: "list ", - Aliases: []string{"ls"}, - Short: "List log subscriptions of a loadbalancer", - Run: cloud.ListCloudLoadbalancerLogSubscriptions, - Args: cobra.ExactArgs(1), - })) - - subscriptionCmd.AddCommand(&cobra.Command{ - Use: "get ", - Short: "Get a specific log subscription", - Run: cloud.GetCloudLoadbalancerLogSubscription, - Args: cobra.ExactArgs(2), - }) - - subscriptionCmd.AddCommand(getLogSubscriptionCreationCmd()) - - subscriptionCmd.AddCommand(&cobra.Command{ - Use: "delete ", - Short: "Delete a log subscription", - Run: cloud.DeleteCloudLoadbalancerLogSubscription, - Args: cobra.ExactArgs(2), - }) -} - -func getLogSubscriptionCreationCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "create ", - Short: "Create a log subscription for a loadbalancer", - Run: cloud.CreateCloudLoadbalancerLogSubscription, - Args: cobra.ExactArgs(1), - } - - cmd.Flags().StringVar(&cloud.CloudLoadbalancerLogSubscriptionCreateSpec.Kind, "kind", "", "Log kind (e.g., haproxy)") - cmd.Flags().StringVar(&cloud.CloudLoadbalancerLogSubscriptionCreateSpec.StreamId, "stream-id", "", "LDP stream ID") - - addParameterFileFlags(cmd, false, assets.CloudOpenapiSchema, "/cloud/project/{serviceName}/region/{regionName}/loadbalancing/loadbalancer/{loadBalancerId}/log/subscription", "post", cloud.LoadbalancerLogSubscriptionCreationExample, nil) - addInteractiveEditorFlag(cmd) - cmd.MarkFlagsMutuallyExclusive("from-file", "editor") - - return cmd -} diff --git a/internal/cmd/cloud_network_test.go b/internal/cmd/cloud_network_test.go index dda71a95..6dfeef58 100644 --- a/internal/cmd/cloud_network_test.go +++ b/internal/cmd/cloud_network_test.go @@ -210,106 +210,3 @@ func (ms *MockSuite) TestCloudPrivateNetworkSubnetCreateCmd(assert, require *td. }`)) } -func (ms *MockSuite) TestCloudLoadbalancerGetCmd(assert, require *td.T) { - httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region", - httpmock.NewStringResponder(200, `["GRA11", "SBG5", "BHS5"]`)) - - httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/GRA11", - httpmock.NewStringResponder(200, `{ - "name": "GRA11", - "type": "region", - "status": "UP", - "services": [ - { - "name": "octavialoadbalancer", - "status": "UP" - } - ], - "countryCode": "fr", - "ipCountries": [], - "continentCode": "NA", - "availabilityZones": [], - "datacenterLocation": "GRA11" - }`)) - - httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5", - httpmock.NewStringResponder(200, `{ - "name": "SBG5", - "type": "region", - "status": "UP", - "services": [ - { - "name": "octavialoadbalancer", - "status": "UP" - } - ], - "countryCode": "fr", - "ipCountries": [], - "continentCode": "NA", - "availabilityZones": [], - "datacenterLocation": "SBG5" - }`)) - - httpmock.RegisterResponder(http.MethodGet, "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/BHS5", - httpmock.NewStringResponder(200, `{ - "name": "BHS5", - "type": "region", - "status": "UP", - "services": [], - "countryCode": "ca", - "ipCountries": [], - "continentCode": "NA", - "availabilityZones": [], - "datacenterLocation": "BHS5" - }`)) - - httpmock.RegisterResponder(http.MethodGet, - "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/loadbalancer/fakeLB", - httpmock.NewStringResponder(200, `{ - "createdAt": "2024-07-30T08:26:51Z", - "flavorId": "f862fa22-6275-4f8f-885e-66a8faf5e44e", - "floatingIp": null, - "id": "334fc97e-a8db-11f0-944d-0050568ce122", - "name": "loadbalancer-sbg5-2024-07-30", - "operatingStatus": "online", - "provisioningStatus": "active", - "region": "SBG5", - "updatedAt": "2025-10-14T08:48:33Z", - "vipAddress": "1.2.3.4", - "vipNetworkId": "3f29f530-a8db-11f0-9ab2-0050568ce122", - "vipSubnetId": "44a869c4-a8db-11f0-899f-0050568ce122" - }`)) - - httpmock.RegisterResponder(http.MethodGet, - "https://eu.api.ovh.com/v1/cloud/project/fakeProjectID/region/SBG5/loadbalancing/flavor/f862fa22-6275-4f8f-885e-66a8faf5e44e", - httpmock.NewStringResponder(200, `{ - "id": "f862fa22-6275-4f8f-885e-66a8faf5e44e", - "name": "medium", - "description": "Medium Load Balancer Flavor" - }`)) - - out, err := cmd.Execute("cloud", "loadbalancer", "get", "fakeLB", "--cloud-project", "fakeProjectID") - require.CmpNoError(err) - assert.Cmp(cleanWhitespacesHelper(out), ` - # 🚀 Load balancer fakeLB - - *loadbalancer-sbg5-2024-07-30* - - ## General information - - **Region**: SBG5 - **Operating status**: online - **Provisioning status**: active - **Flavor**: medium (ID: f862fa22-6275-4f8f-885e-66a8faf5e44e) - **Creation date**: 2024-07-30T08:26:51Z - - ## Technical information - - **VIP address**: 1.2.3.4 - **VIP network ID**: 3f29f530-a8db-11f0-9ab2-0050568ce122 - **VIP subnet ID**: 44a869c4-a8db-11f0-899f-0050568ce122 - - 💡 Use option -o json or -o yaml to get the raw output with all information - -`) -} diff --git a/internal/services/browser/api.go b/internal/services/browser/api.go index e5a9b6eb..ae8d38a8 100644 --- a/internal/services/browser/api.go +++ b/internal/services/browser/api.go @@ -85,7 +85,7 @@ func (m Model) fetchDataForPath(path string) tea.Cmd { msg.forProduct = product return msg } - case "/networks/loadbalancer": + case "/loadbalancer": return func() tea.Msg { msg := m.fetchLoadBalancersData() msg.forProduct = product