Skip to content

Commit 7d0788e

Browse files
committed
feat: liqoctl gateway template check
1 parent b2ac20b commit 7d0788e

File tree

10 files changed

+218
-47
lines changed

10 files changed

+218
-47
lines changed

cmd/liqoctl/cmd/network.go

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,7 @@ func newNetworkConnectCommand(ctx context.Context, options *network.Options) *co
139139
Args: cobra.NoArgs,
140140

141141
Run: func(_ *cobra.Command, _ []string) {
142-
err := options.RunConnect(ctx)
143-
if err != nil {
144-
options.LocalFactory.Printer.CheckErr(
145-
fmt.Errorf("`network connect` failed (error: %w). Issue `network disconnect` to cleanup the environment", err))
146-
}
147-
output.ExitOnErr(err)
142+
output.ExitOnErr(options.RunConnect(ctx))
148143
},
149144
}
150145

@@ -153,18 +148,18 @@ func newNetworkConnectCommand(ctx context.Context, options *network.Options) *co
153148
"Type of Gateway Server. Leave empty to use default Liqo implementation of WireGuard")
154149
cmd.Flags().StringVar(&options.ServerTemplateName, "server-template-name", forge.DefaultGwServerTemplateName,
155150
"Name of the Gateway Server template")
156-
cmd.Flags().StringVar(&options.ServerTemplateNamespace, "server-template-namespace", "",
151+
cmd.Flags().StringVar(&options.ServerTemplateNamespace, "server-template-namespace", forge.DefaultGwServerTemplateNamespace,
157152
"Namespace of the Gateway Server template")
158153
cmd.Flags().Var(options.ServerServiceType, "server-service-type",
159-
fmt.Sprintf("Service type of the Gateway Server. Default: %s."+
154+
fmt.Sprintf("Service type of the Gateway Server service. Default: %s."+
160155
" Note: use ClusterIP only if you know what you are doing and you have a proper network configuration",
161156
forge.DefaultGwServerServiceType))
162-
cmd.Flags().Int32Var(&options.ServerPort, "server-port", forge.DefaultGwServerPort,
163-
fmt.Sprintf("Port of the Gateway Server. Default: %d", forge.DefaultGwServerPort))
164-
cmd.Flags().Int32Var(&options.ServerNodePort, "node-port", 0,
165-
"Force the NodePort of the Gateway Server. Leave empty to let Kubernetes allocate a random NodePort")
166-
cmd.Flags().StringVar(&options.ServerLoadBalancerIP, "load-balancer-ip", "",
167-
"Force LoadBalancer IP of the Gateway Server. Leave empty to use the one provided by the LoadBalancer provider")
157+
cmd.Flags().Int32Var(&options.ServerServicePort, "server-service-port", forge.DefaultGwServerPort,
158+
fmt.Sprintf("Port of the Gateway Server service. Default: %d", forge.DefaultGwServerPort))
159+
cmd.Flags().Int32Var(&options.ServerServiceNodePort, "server-service-nodeport", 0,
160+
"Force the NodePort of the Gateway Server service. Leave empty to let Kubernetes allocate a random NodePort")
161+
cmd.Flags().StringVar(&options.ServerServiceLoadBalancerIP, "server-service-loadbalancerip", "",
162+
"Force LoadBalancer IP of the Gateway Server service. Leave empty to use the one provided by the LoadBalancer provider")
168163

169164
// Client flags
170165
cmd.Flags().StringVar(&options.ClientGatewayType, "client-type", forge.DefaultGwClientType,

cmd/liqoctl/cmd/peer.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,15 @@ func newPeerCommand(ctx context.Context, f *factory.Factory) *cobra.Command {
9090
// Networking flags
9191
cmd.Flags().BoolVar(&options.NetworkingDisabled, "networking-disabled", false, "Disable networking between the two clusters")
9292
cmd.Flags().Var(options.ServerServiceType, "server-service-type",
93-
fmt.Sprintf("Service type of the Gateway Server. Default: %s."+
93+
fmt.Sprintf("Service type of the Gateway Server service. Default: %s."+
9494
" Note: use ClusterIP only if you know what you are doing and you have a proper network configuration",
9595
nwforge.DefaultGwServerServiceType))
96-
cmd.Flags().Int32Var(&options.ServerPort, "server-port", nwforge.DefaultGwServerPort,
97-
fmt.Sprintf("Port of the Gateway Server. Default: %d", nwforge.DefaultGwServerPort))
96+
cmd.Flags().Int32Var(&options.ServerServicePort, "server-service-port", nwforge.DefaultGwServerPort,
97+
fmt.Sprintf("Port of the Gateway Server service. Default: %d", nwforge.DefaultGwServerPort))
98+
cmd.Flags().Int32Var(&options.ServerServiceNodePort, "server-service-nodeport", 0,
99+
"Force the NodePort of the Gateway Server service. Leave empty to let Kubernetes allocate a random NodePort")
100+
cmd.Flags().StringVar(&options.ServerServiceLoadBalancerIP, "server-service-loadbalancerip", "",
101+
"IP of the LoadBalancer for the Gateway Server service")
98102
cmd.Flags().IntVar(&options.MTU, "mtu", nwforge.DefaultMTU,
99103
fmt.Sprintf("MTU of the Gateway server and client. Default: %d", nwforge.DefaultMTU))
100104

deployments/liqo/templates/liqo-wireguard-gateway-server-template.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ spec:
3636
- port: "{{"{{ .Spec.Endpoint.Port }}"}}"
3737
protocol: UDP
3838
targetPort: "{{"{{ .Spec.Endpoint.Port }}"}}"
39+
3940
{{- if .Values.networking.gatewayTemplates.server.service.allocateLoadBalancerNodePorts }}
4041
allocateLoadBalancerNodePorts: {{ .Values.networking.gatewayTemplates.server.service.allocateLoadBalancerNodePorts }}
4142
{{- end }}

pkg/liqo-controller-manager/networking/forge/gatewayserver.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@ import (
2828

2929
// Default values for the GatewayServer.
3030
const (
31-
DefaultGwServerType = "networking.liqo.io/v1beta1/wggatewayservertemplates"
32-
DefaultGwServerTemplateName = "wireguard-server"
33-
DefaultGwServerServiceType = corev1.ServiceTypeLoadBalancer
34-
DefaultGwServerPort = 51840
35-
DefaultKeysDir = "/etc/wireguard/keys"
31+
DefaultGwServerType = "networking.liqo.io/v1beta1/wggatewayservertemplates"
32+
DefaultGwServerTemplateName = "wireguard-server"
33+
DefaultGwServerTemplateNamespace = "liqo"
34+
DefaultGwServerServiceType = corev1.ServiceTypeLoadBalancer
35+
DefaultGwServerPort = 51840
36+
DefaultKeysDir = "/etc/wireguard/keys"
3637
)
3738

3839
// defaultGatewayServerName returns the default name for a GatewayServer.

pkg/liqoctl/factory/factory.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/spf13/pflag"
2323
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
2424
"k8s.io/cli-runtime/pkg/genericclioptions"
25+
"k8s.io/client-go/dynamic"
2526
"k8s.io/client-go/kubernetes"
2627
"k8s.io/client-go/rest"
2728
cmdutil "k8s.io/kubectl/pkg/cmd/util"
@@ -58,6 +59,8 @@ type Factory struct {
5859

5960
// Printer is the object used to output messages in the appropriate format.
6061
Printer *output.Printer
62+
// PrinterGlobal is the object used to output messages in the appropriate format. It is not scoped to local or remote cluster.
63+
PrinterGlobal *output.Printer
6164
// SkipConfirm determines whether to skip confirmations.
6265
SkipConfirm bool
6366
// Whether to add a scope to the printer (i.e., local/remote).
@@ -79,6 +82,9 @@ type Factory struct {
7982
// kubeClient is a Kubernetes clientset for interacting with the base Kubernetes APIs.
8083
KubeClient kubernetes.Interface
8184

85+
// DynCLient
86+
DynClient *dynamic.DynamicClient
87+
8288
helmClient helm.Client
8389
}
8490

@@ -209,6 +215,8 @@ func (f *Factory) Initialize(opts ...Options) (err error) {
209215
f.Printer = output.NewLocalPrinter(o.scoped, verbose)
210216
}
211217

218+
f.PrinterGlobal = output.NewGlobalPrinter(o.scoped, verbose)
219+
212220
if f.Namespace == "" {
213221
f.Namespace, _, err = f.factory.ToRawKubeConfigLoader().Namespace()
214222
if err != nil {
@@ -232,6 +240,11 @@ func (f *Factory) Initialize(opts ...Options) (err error) {
232240
return err
233241
}
234242

243+
f.DynClient, err = dynamic.NewForConfig(f.RESTConfig)
244+
if err != nil {
245+
return err
246+
}
247+
235248
// Leverage the REST mapper retrieved from the factory, to defer the loading of the mappings until the first API
236249
// request is made. This prevents errors in case of invalid kubeconfigs, if no interaction is required.
237250
f.CRClient, err = client.New(f.RESTConfig, client.Options{Mapper: restMapper})

pkg/liqoctl/network/cluster.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
corev1 "k8s.io/api/core/v1"
2222
apierrors "k8s.io/apimachinery/pkg/api/errors"
2323
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2425
"k8s.io/apimachinery/pkg/types"
2526
"sigs.k8s.io/controller-runtime/pkg/client"
2627
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
@@ -29,6 +30,7 @@ import (
2930
networkingv1beta1 "github.com/liqotech/liqo/apis/networking/v1beta1"
3031
"github.com/liqotech/liqo/pkg/consts"
3132
gwforge "github.com/liqotech/liqo/pkg/gateway/forge"
33+
enutils "github.com/liqotech/liqo/pkg/liqo-controller-manager/networking/external-network/utils"
3234
"github.com/liqotech/liqo/pkg/liqo-controller-manager/networking/forge"
3335
networkingutils "github.com/liqotech/liqo/pkg/liqo-controller-manager/networking/utils"
3436
"github.com/liqotech/liqo/pkg/liqoctl/factory"
@@ -37,6 +39,7 @@ import (
3739
tenantnamespace "github.com/liqotech/liqo/pkg/tenantNamespace"
3840
liqoutils "github.com/liqotech/liqo/pkg/utils"
3941
"github.com/liqotech/liqo/pkg/utils/getters"
42+
"github.com/liqotech/liqo/pkg/utils/maps"
4043
)
4144

4245
// Cluster contains the information about a cluster.
@@ -194,6 +197,123 @@ func (c *Cluster) SetupConfiguration(ctx context.Context, conf *networkingv1beta
194197
return nil
195198
}
196199

200+
// CheckTemplateGwClient checks if the GatewayClient template is correctly set up.
201+
func (c *Cluster) CheckTemplateGwClient(ctx context.Context, opts *Options) error {
202+
templateName := opts.ServerTemplateName
203+
templateNamespace := opts.ServerTemplateNamespace
204+
templateGVR := opts.ServerGatewayType
205+
206+
s := c.local.Printer.StartSpinner(fmt.Sprintf("Checking gateway client template \"%s/%s\"", templateName, templateNamespace))
207+
208+
_, err := c.checkTemplate(ctx, templateName, templateNamespace, templateGVR)
209+
if err != nil {
210+
s.Fail(fmt.Sprintf("An error occurred while checking gateway template \"%s/%s\": %v", templateName, templateNamespace, output.PrettyErr(err)))
211+
return err
212+
}
213+
214+
s.Success(fmt.Sprintf("Gateway client template \"%s/%s\" correctly checked", templateName, templateNamespace))
215+
return nil
216+
}
217+
218+
// CheckTemplateGwServer checks if the GatewayServer template is correctly set up.
219+
func (c *Cluster) CheckTemplateGwServer(ctx context.Context, opts *Options) error {
220+
templateName := opts.ServerTemplateName
221+
templateNamespace := opts.ServerTemplateNamespace
222+
templateGVR := opts.ServerGatewayType
223+
224+
s := c.local.Printer.StartSpinner(fmt.Sprintf("Checking gateway client template \"%s/%s\"", templateName, templateNamespace))
225+
226+
template, err := c.checkTemplate(ctx, templateName, templateNamespace, templateGVR)
227+
if err != nil {
228+
s.Fail(fmt.Sprintf("An error occurred while checking gateway template \"%s/%s\": %v", templateName, templateNamespace, output.PrettyErr(err)))
229+
return err
230+
}
231+
232+
if err := c.checkTemplateServerService(template, opts); err != nil {
233+
s.Fail(fmt.Sprintf("An error occurred while checking gateway template \"%s/%s\": %v", templateName, templateNamespace, output.PrettyErr(err)))
234+
return err
235+
}
236+
237+
s.Success(fmt.Sprintf("Gateway client template \"%s/%s\" correctly checked", templateName, templateNamespace))
238+
return nil
239+
}
240+
241+
func (c *Cluster) checkTemplate(ctx context.Context, templateName, templateNamespace, templateGvr string) (*unstructured.Unstructured, error) {
242+
// Server Template Reference
243+
gvr, err := enutils.ParseGroupVersionResource(templateGvr)
244+
if err != nil {
245+
return nil, err
246+
}
247+
248+
template, err := c.local.DynClient.Resource(gvr).Namespace(templateNamespace).Get(ctx, templateName, metav1.GetOptions{})
249+
if err != nil {
250+
return nil, err
251+
}
252+
253+
return template, nil
254+
}
255+
256+
// checkTemplateServerService checks if the GatewayServer service template is correctly set up.
257+
func (c *Cluster) checkTemplateServerService(template *unstructured.Unstructured, opts *Options) error {
258+
switch corev1.ServiceType(opts.ServerServiceType.Value) {
259+
case corev1.ServiceTypeClusterIP:
260+
return c.checkTemplateServerServiceClusterIP(template, opts)
261+
case corev1.ServiceTypeNodePort:
262+
return c.checkTemplateServerServiceNodePort(template, opts)
263+
case corev1.ServiceTypeLoadBalancer:
264+
return c.checkTemplateServerServiceLoadBalancer(template, opts)
265+
case corev1.ServiceTypeExternalName:
266+
return fmt.Errorf("externalName service type not supported")
267+
}
268+
return nil
269+
}
270+
271+
func (c *Cluster) checkTemplateServerServiceClusterIP(_ *unstructured.Unstructured, _ *Options) error {
272+
return nil
273+
}
274+
func (c *Cluster) checkTemplateServerServiceNodePort(template *unstructured.Unstructured, opts *Options) error {
275+
if opts.ServerServiceNodePort == 0 {
276+
return nil
277+
}
278+
279+
path := "spec.template.spec.service.spec.ports"
280+
templateServicePorts, err := maps.GetNestedField(template.Object, path)
281+
if err != nil {
282+
return fmt.Errorf("unable to get %s of the server template", path)
283+
}
284+
285+
templateServicePortsSlice, ok := templateServicePorts.([]interface{})
286+
if !ok {
287+
return fmt.Errorf("unable to cast %s to []interface{}", path)
288+
}
289+
290+
port, ok := templateServicePortsSlice[0].(map[string]interface{})
291+
if !ok {
292+
return fmt.Errorf("unable to cast %s to map[string]interface{}", path)
293+
}
294+
295+
_, err = maps.GetNestedField(port, "nodePort")
296+
if err != nil {
297+
return fmt.Errorf("unable to get spec.template.spec.service.spec.ports[0].nodePort int the server template, " +
298+
"since you specified the flag \"--server-service-nodeport\" you need to add the \"nodePort\" field in the template")
299+
}
300+
301+
return nil
302+
}
303+
func (c *Cluster) checkTemplateServerServiceLoadBalancer(template *unstructured.Unstructured, opts *Options) error {
304+
if opts.ServerServiceLoadBalancerIP == "" {
305+
return nil
306+
}
307+
308+
path := "spec.template.spec.service.spec.loadBalancerIP"
309+
_, err := maps.GetNestedField(template.Object, path)
310+
if err != nil {
311+
return fmt.Errorf("unable to get %s of the server template, "+
312+
"since you specified the flag \"--server-service-loadbalancerip\" you need to add the \"loadBalancerIP\" field in the template", path)
313+
}
314+
return nil
315+
}
316+
197317
// CheckNetworkInitialized checks if the network is initialized correctly.
198318
func (c *Cluster) CheckNetworkInitialized(ctx context.Context, remoteClusterID liqov1beta1.ClusterID) error {
199319
s := c.local.Printer.StartSpinner("Checking network is initialized correctly")

pkg/liqoctl/network/handler.go

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,13 @@ type Options struct {
3838
Timeout time.Duration
3939
Wait bool
4040

41-
ServerGatewayType string
42-
ServerTemplateName string
43-
ServerTemplateNamespace string
44-
ServerServiceType *argsutils.StringEnum
45-
ServerPort int32
46-
ServerNodePort int32
47-
ServerLoadBalancerIP string
41+
ServerGatewayType string
42+
ServerTemplateName string
43+
ServerTemplateNamespace string
44+
ServerServiceType *argsutils.StringEnum
45+
ServerServicePort int32
46+
ServerServiceNodePort int32
47+
ServerServiceLoadBalancerIP string
4848

4949
ClientGatewayType string
5050
ClientTemplateName string
@@ -165,6 +165,16 @@ func (o *Options) RunConnect(ctx context.Context) error {
165165
return err
166166
}
167167

168+
// Check if the Templates exists and is valid on cluster 1
169+
if err := cluster1.CheckTemplateGwClient(ctx, o); err != nil {
170+
return err
171+
}
172+
173+
// Check if the Templates exists and is valid on cluster 2
174+
if err := cluster2.CheckTemplateGwServer(ctx, o); err != nil {
175+
return err
176+
}
177+
168178
// Check if the Networking is initialized on cluster 1
169179
if err := cluster1.CheckNetworkInitialized(ctx, cluster2.localClusterID); err != nil {
170180
return err
@@ -331,9 +341,9 @@ func (o *Options) newGatewayServerForgeOptions(kubeClient kubernetes.Interface,
331341
TemplateNamespace: o.ServerTemplateNamespace,
332342
ServiceType: corev1.ServiceType(o.ServerServiceType.Value),
333343
MTU: o.MTU,
334-
Port: o.ServerPort,
335-
NodePort: ptr.To(o.ServerNodePort),
336-
LoadBalancerIP: ptr.To(o.ServerLoadBalancerIP),
344+
Port: o.ServerServicePort,
345+
NodePort: ptr.To(o.ServerServiceNodePort),
346+
LoadBalancerIP: ptr.To(o.ServerServiceLoadBalancerIP),
337347
}
338348
}
339349

pkg/liqoctl/output/output.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,11 @@ func NewRemotePrinter(scoped, verbose bool) *Printer {
264264
return newPrinter(remoteClusterName, remoteClusterColor, scoped, verbose)
265265
}
266266

267+
// NewGlobalPrinter returns a new printer referring to the global scope.
268+
func NewGlobalPrinter(scoped, verbose bool) *Printer {
269+
return newPrinter("global", pterm.FgDefault, scoped, verbose)
270+
}
271+
267272
func newPrinter(scope string, color pterm.Color, scoped, verbose bool) *Printer {
268273
generic := &pterm.PrefixPrinter{MessageStyle: pterm.NewStyle(pterm.FgDefault)}
269274

0 commit comments

Comments
 (0)