1package loadbalancer
2
3import (
4	"fmt"
5
6	"github.com/hetznercloud/cli/internal/cmd/cmpl"
7	"github.com/hetznercloud/cli/internal/cmd/util"
8	"github.com/hetznercloud/cli/internal/state"
9	"github.com/hetznercloud/hcloud-go/hcloud"
10	"github.com/spf13/cobra"
11)
12
13func newAddServiceCommand(cli *state.State) *cobra.Command {
14	cmd := &cobra.Command{
15		Use:                   "add-service LOADBALANCER FLAGS",
16		Short:                 "Add a service from a Load Balancer",
17		Args:                  cobra.ExactArgs(1),
18		ValidArgsFunction:     cmpl.SuggestArgs(cmpl.SuggestCandidatesF(cli.LoadBalancerNames)),
19		TraverseChildren:      true,
20		DisableFlagsInUseLine: true,
21		PreRunE:               util.ChainRunE(validateAddService, cli.EnsureToken),
22		RunE:                  cli.Wrap(runAddService),
23	}
24	cmd.Flags().String("protocol", "", "Protocol of the service (required)")
25	cmd.MarkFlagRequired("protocol")
26
27	cmd.Flags().Int("listen-port", 0, "Listen port of the service")
28	cmd.Flags().Int("destination-port", 0, "Destination port of the service on the targets")
29	cmd.Flags().Bool("proxy-protocol", false, "Enable proxyprotocol")
30
31	cmd.Flags().Bool("http-sticky-sessions", false, "Enable Sticky Sessions")
32	cmd.Flags().String("http-cookie-name", "", "Sticky Sessions: Cookie Name we set")
33	cmd.Flags().Duration("http-cookie-lifetime", 0, "Sticky Sessions: Lifetime of the cookie")
34	cmd.Flags().IntSlice("http-certificates", []int{}, "ID of Certificates which are attached to this Load Balancer")
35	cmd.Flags().Bool("http-redirect-http", false, "Redirect all traffic on port 80 to port 443")
36
37	return cmd
38}
39
40func validateAddService(cmd *cobra.Command, args []string) error {
41	protocol, _ := cmd.Flags().GetString("protocol")
42	listenPort, _ := cmd.Flags().GetInt("listen-port")
43	destinationPort, _ := cmd.Flags().GetInt("destination-port")
44	httpCertificates, _ := cmd.Flags().GetIntSlice("http-certificates")
45
46	if protocol == "" {
47		return fmt.Errorf("required flag protocol not set")
48	}
49
50	switch hcloud.LoadBalancerServiceProtocol(protocol) {
51	case hcloud.LoadBalancerServiceProtocolHTTP:
52		break
53	case hcloud.LoadBalancerServiceProtocolTCP:
54		if listenPort == 0 {
55			return fmt.Errorf("please specify a listen port")
56		}
57
58		if destinationPort == 0 {
59			return fmt.Errorf("please specify a destination port")
60		}
61		break
62	case hcloud.LoadBalancerServiceProtocolHTTPS:
63		if len(httpCertificates) == 0 {
64			return fmt.Errorf("no certificate specified")
65		}
66	default:
67		return fmt.Errorf("%s is not a valid protocol", protocol)
68	}
69	if listenPort > 65535 {
70		return fmt.Errorf("%d is not a valid listen port", listenPort)
71	}
72
73	if destinationPort > 65535 {
74		return fmt.Errorf("%d is not a valid destination port", destinationPort)
75	}
76	return nil
77}
78
79func runAddService(cli *state.State, cmd *cobra.Command, args []string) error {
80	idOrName := args[0]
81	protocol, _ := cmd.Flags().GetString("protocol")
82	listenPort, _ := cmd.Flags().GetInt("listen-port")
83	destinationPort, _ := cmd.Flags().GetInt("destination-port")
84	proxyProtocol, _ := cmd.Flags().GetBool("proxy-protocol")
85
86	httpStickySessions, _ := cmd.Flags().GetBool("http-sticky-sessions")
87	httpCookieName, _ := cmd.Flags().GetString("http-cookie-name")
88	httpCookieLifetime, _ := cmd.Flags().GetDuration("http-cookie-lifetime")
89	httpCertificates, _ := cmd.Flags().GetIntSlice("http-certificates")
90	httpRedirect, _ := cmd.Flags().GetBool("http-redirect-http")
91
92	loadBalancer, _, err := cli.Client().LoadBalancer.Get(cli.Context, idOrName)
93	if err != nil {
94		return err
95	}
96	if loadBalancer == nil {
97		return fmt.Errorf("Load Balancer not found: %s", idOrName)
98	}
99
100	opts := hcloud.LoadBalancerAddServiceOpts{
101		Protocol:      hcloud.LoadBalancerServiceProtocol(protocol),
102		Proxyprotocol: hcloud.Bool(proxyProtocol),
103	}
104
105	if listenPort != 0 {
106		opts.ListenPort = hcloud.Int(listenPort)
107	}
108	if destinationPort != 0 {
109		opts.DestinationPort = hcloud.Int(destinationPort)
110	}
111
112	if protocol != string(hcloud.LoadBalancerServiceProtocolTCP) {
113		opts.HTTP = &hcloud.LoadBalancerAddServiceOptsHTTP{
114			StickySessions: hcloud.Bool(httpStickySessions),
115			RedirectHTTP:   hcloud.Bool(httpRedirect),
116		}
117		if httpCookieName != "" {
118			opts.HTTP.CookieName = hcloud.String(httpCookieName)
119		}
120		if httpCookieLifetime != 0 {
121			opts.HTTP.CookieLifetime = hcloud.Duration(httpCookieLifetime)
122		}
123		for _, certificateID := range httpCertificates {
124			opts.HTTP.Certificates = append(opts.HTTP.Certificates, &hcloud.Certificate{ID: certificateID})
125		}
126	}
127	action, _, err := cli.Client().LoadBalancer.AddService(cli.Context, loadBalancer, opts)
128	if err != nil {
129		return err
130	}
131	if err := cli.ActionProgress(cli.Context, action); err != nil {
132		return err
133	}
134	fmt.Printf("Service was added to Load Balancer %d\n", loadBalancer.ID)
135
136	return nil
137}
138