Skip to content

Commit 3b9f887

Browse files
claudioloradamjensenbot
authored andcommitted
Add liqoctl info peer command
1 parent 7bf011f commit 3b9f887

File tree

29 files changed

+2265
-138
lines changed

29 files changed

+2265
-138
lines changed

cmd/liqoctl/cmd/info.go

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/liqotech/liqo/pkg/liqoctl/factory"
2626
"github.com/liqotech/liqo/pkg/liqoctl/info"
2727
"github.com/liqotech/liqo/pkg/liqoctl/info/localstatus"
28+
"github.com/liqotech/liqo/pkg/liqoctl/info/peer"
2829
"github.com/liqotech/liqo/pkg/liqoctl/output"
2930
"github.com/liqotech/liqo/pkg/utils/args"
3031
)
@@ -54,6 +55,34 @@ get a specific field
5455
$ {{ .Executable }} info --get network.podcidr
5556
`
5657

58+
const liqoctlInfoPeerLongHelp = `Show additional info about peered clusters.
59+
60+
Liqoctl provides a set of commands to verify the status of the Liqo control
61+
plane, its configuration, as well as the characteristics of the currently
62+
active peerings, and reports the outcome in human-readable or
63+
machine-readable format (either JSON or YAML).
64+
Additionally, via '--get', it allows to retrieve each single field of the reports
65+
using a query in dot notation (e.g. '--get field.subfield')
66+
67+
This command shows additional information about the peered clusters, the status
68+
of the modules and the amount of shared resources.
69+
70+
Examples:
71+
$ {{ .Executable }} info peer
72+
or
73+
$ {{ .Executable }} info peer cluster1
74+
or
75+
$ {{ .Executable }} info peer cluster1 cluster2
76+
or
77+
$ {{ .Executable }} info peer cluster1 cluster2 --namespace liqo-system
78+
show the output in YAML format
79+
$ {{ .Executable }} info peer -o yaml
80+
get a specific field
81+
$ {{ .Executable }} info peer cluster1 cluster2 --get cluster2.network.cidr
82+
when a single cluster is specified, the cluster ID at the beginning of the query can be omitted
83+
$ {{ .Executable }} info peer cluster1 --get network.cidr
84+
`
85+
5786
func infoPreRun(options *info.Options) {
5887
// When the output is redirected to a file is desiderable that errors ends in the stderr output.
5988
options.Printer.Error.Writer = os.Stderr
@@ -70,17 +99,23 @@ func infoPreRun(options *info.Options) {
7099
func newPeerInfoCommand(ctx context.Context, f *factory.Factory, options *info.Options) *cobra.Command {
71100
cmd := &cobra.Command{
72101
Use: "peer",
73-
Short: "Show additional info about one or more specific peerings",
74-
Long: WithTemplate(""),
75-
Args: cobra.MinimumNArgs(1),
102+
Short: "Show additional info about peered clusters",
103+
Long: WithTemplate(liqoctlInfoPeerLongHelp),
76104
ValidArgsFunction: completion.ClusterIDs(ctx, f, completion.NoLimit),
77105

78106
PreRun: func(_ *cobra.Command, _ []string) {
79107
infoPreRun(options)
80108
},
81109

82110
Run: func(_ *cobra.Command, clusterIds []string) {
83-
output.ExitOnErr(options.RunPeerInfo(ctx, clusterIds))
111+
checkers := []info.MultiClusterChecker{
112+
&peer.InfoChecker{},
113+
&peer.NetworkChecker{},
114+
&peer.AuthChecker{},
115+
&peer.OffloadingChecker{},
116+
}
117+
118+
output.ExitOnErr(options.RunPeerInfo(ctx, checkers, clusterIds))
84119
},
85120
}
86121

pkg/liqo-controller-manager/networking/external-network/route/k8s.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818
"context"
1919
"fmt"
2020

21-
kerrors "k8s.io/apimachinery/pkg/api/errors"
2221
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2322
"k8s.io/apimachinery/pkg/labels"
2423
"k8s.io/apimachinery/pkg/runtime"
@@ -141,13 +140,8 @@ func forgeMutateRouteConfiguration(cfg *networkingv1beta1.Configuration,
141140

142141
// GetGatewayMode returns the mode of the Gateway related to the Configuration.
143142
func GetGatewayMode(ctx context.Context, cl client.Client, remoteClusterID liqov1beta1.ClusterID) (gateway.Mode, error) {
144-
gwclient, err := getters.GetGatewayClientByClusterID(ctx, cl, remoteClusterID)
145-
if err != nil && !kerrors.IsNotFound(err) {
146-
return "", err
147-
}
148-
149-
gwserver, err := getters.GetGatewayServerByClusterID(ctx, cl, remoteClusterID)
150-
if err != nil && !kerrors.IsNotFound(err) {
143+
gwserver, gwclient, err := getters.GetGatewaysByClusterID(ctx, cl, remoteClusterID)
144+
if err != nil {
151145
return "", err
152146
}
153147

pkg/liqoctl/info/checker.go

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,37 +14,56 @@
1414

1515
package info
1616

17-
import "context"
17+
import (
18+
"context"
1819

19-
// Checker is the interface to be implemented by all the checkers that
20-
// collect info about the current instance of Liqo.
21-
type Checker interface {
20+
liqov1beta1 "github.com/liqotech/liqo/apis/core/v1beta1"
21+
)
22+
23+
// CheckerBase is the base interface for the multiple types of checkers.
24+
type CheckerBase interface {
2225
// Collect the data from the Liqo installation
2326
Collect(ctx context.Context, options Options)
24-
// Return the collected data using a user friendly output
25-
Format(options Options) string
2627
// Get the title of the section retrieve by the checker
2728
GetTitle() string
2829
// Get the id to be shown of machine readable output
2930
GetID() string
30-
// Get the data collected by the checker
31-
GetData() interface{}
3231
// Return the errors occurred during the collection of the data.
3332
GetCollectionErrors() []error
3433
}
3534

36-
// CheckerBase contains the common attributes and functions of the checkers.
37-
type CheckerBase struct {
35+
// Checker is the interface to be implemented by all the checkers that
36+
// collect info about the current instance of Liqo.
37+
type Checker interface {
38+
CheckerBase
39+
// Return the collected data using a user friendly output
40+
Format(options Options) string
41+
// Get the data collected by the checker
42+
GetData() interface{}
43+
}
44+
45+
// MultiClusterChecker is the interface to be implemented by all the checkers that
46+
// collect info from multiple clusters.
47+
type MultiClusterChecker interface {
48+
CheckerBase
49+
// Get the data collected by the checker for the specified clusterID
50+
GetDataByClusterID(clusterID liqov1beta1.ClusterID) (interface{}, error)
51+
// Return the collected data for the specified clusterID using a user friendly output
52+
FormatForClusterID(clusterID liqov1beta1.ClusterID, options Options) string
53+
}
54+
55+
// CheckerCommon contains the common attributes and functions of the checkers.
56+
type CheckerCommon struct {
3857
collectionErrors []error
3958
}
4059

4160
// AddCollectionError adds an error to the list of errors occurred while
4261
// collecting the info about a Liqo component.
43-
func (c *CheckerBase) AddCollectionError(err error) {
62+
func (c *CheckerCommon) AddCollectionError(err error) {
4463
c.collectionErrors = append(c.collectionErrors, err)
4564
}
4665

4766
// GetCollectionErrors returns the errors occurred during the collection of the data.
48-
func (c *CheckerBase) GetCollectionErrors() []error {
67+
func (c *CheckerCommon) GetCollectionErrors() []error {
4968
return c.collectionErrors
5069
}

pkg/liqoctl/info/common/doc.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2019-2024 The Liqo Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
16+
// Package common contains the utilities and the types common to the checkers
17+
package common

pkg/liqoctl/info/common/types.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2019-2024 The Liqo Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
16+
package common
17+
18+
// ModuleStatus represents the status of each of the modules.
19+
type ModuleStatus string
20+
21+
const (
22+
// ModuleHealthy indicates a module that works as expected.
23+
ModuleHealthy ModuleStatus = "Healthy"
24+
// ModuleUnhealthy indicates that there are issues with the module.
25+
ModuleUnhealthy ModuleStatus = "Unhealthy"
26+
// ModuleDisabled indicates that the modules is not currently used.
27+
ModuleDisabled ModuleStatus = "Disabled"
28+
)

pkg/liqoctl/info/common/utils.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright 2019-2024 The Liqo Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
16+
package common
17+
18+
import (
19+
"github.com/pterm/pterm"
20+
21+
liqov1beta1 "github.com/liqotech/liqo/apis/core/v1beta1"
22+
)
23+
24+
var statusStyles = map[ModuleStatus]*pterm.Style{
25+
ModuleHealthy: pterm.NewStyle(pterm.FgGreen, pterm.Bold),
26+
ModuleUnhealthy: pterm.NewStyle(pterm.FgRed, pterm.Bold),
27+
ModuleDisabled: pterm.NewStyle(pterm.FgLightCyan, pterm.Bold),
28+
}
29+
30+
// CheckModuleStatusAndAlerts returns the status and the alerts of the given module.
31+
func CheckModuleStatusAndAlerts(module liqov1beta1.Module) (status ModuleStatus, alerts []string) {
32+
status = CheckModuleStatus(module)
33+
// Collect the alerts if status if module is not healthy
34+
if status == ModuleUnhealthy {
35+
alerts = GetModuleAlerts(module)
36+
}
37+
return
38+
}
39+
40+
// CheckModuleStatus based on the conditions of a module returns its status.
41+
func CheckModuleStatus(module liqov1beta1.Module) ModuleStatus {
42+
if module.Enabled {
43+
for i := range module.Conditions {
44+
condition := &module.Conditions[i]
45+
46+
if condition.Status != liqov1beta1.ConditionStatusEstablished && condition.Status != liqov1beta1.ConditionStatusReady {
47+
return ModuleUnhealthy
48+
}
49+
}
50+
return ModuleHealthy
51+
}
52+
53+
return ModuleDisabled
54+
}
55+
56+
// GetModuleAlerts returns the alerts for the given module.
57+
func GetModuleAlerts(module liqov1beta1.Module) []string {
58+
alerts := []string{}
59+
for _, condition := range module.Conditions {
60+
if condition.Status != liqov1beta1.ConditionStatusEstablished && condition.Status != liqov1beta1.ConditionStatusReady {
61+
alerts = append(alerts, condition.Message)
62+
}
63+
}
64+
return alerts
65+
}
66+
67+
// FormatStatus returns a formatted string with the provided status.
68+
func FormatStatus(moduleStatus ModuleStatus) string {
69+
style := statusStyles[moduleStatus]
70+
return style.Sprint(moduleStatus)
71+
}

pkg/liqoctl/info/handler.go

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ package info
1818
import (
1919
"context"
2020
"fmt"
21+
"maps"
22+
"slices"
2123

24+
liqov1beta1 "github.com/liqotech/liqo/apis/core/v1beta1"
2225
"github.com/liqotech/liqo/pkg/liqoctl/factory"
2326
)
2427

@@ -41,9 +44,10 @@ var LocalInfoQueryShortcuts = map[string]string{
4144
type Options struct {
4245
*factory.Factory
4346

44-
Verbose bool
45-
Format OutputFormat
46-
GetQuery string
47+
Verbose bool
48+
Format OutputFormat
49+
GetQuery string
50+
ClustersInfo map[liqov1beta1.ClusterID]*liqov1beta1.ForeignCluster
4751
}
4852

4953
// NewOptions returns a new Options struct.
@@ -80,9 +84,11 @@ func (o *Options) RunInfo(ctx context.Context, checkers []Checker) error {
8084
return nil
8185
// If query specified try to retrieve the field from the output
8286
case o.GetQuery != "":
83-
output, err = o.sPrintField(o.GetQuery, checkers, LocalInfoQueryShortcuts)
87+
data := o.collectDataFromCheckers(checkers)
88+
output, err = o.sPrintField(o.GetQuery, data, LocalInfoQueryShortcuts)
8489
default:
85-
output, err = o.sPrintMachineReadable(checkers)
90+
data := o.collectDataFromCheckers(checkers)
91+
output, err = o.sPrintMachineReadable(data)
8692
}
8793

8894
if err != nil {
@@ -95,6 +101,71 @@ func (o *Options) RunInfo(ctx context.Context, checkers []Checker) error {
95101
}
96102

97103
// RunPeerInfo execute the `info peer` command.
98-
func (o *Options) RunPeerInfo(_ context.Context, _ []string) error {
99-
panic("Not implemented")
104+
func (o *Options) RunPeerInfo(ctx context.Context, checkers []MultiClusterChecker, clusterIDs []string) error {
105+
// Check whether Liqo is installed in the current cluster
106+
if err := o.installationCheck(ctx); err != nil {
107+
return err
108+
}
109+
110+
if err := o.getForeignClusters(ctx, clusterIDs); err != nil {
111+
return err
112+
}
113+
114+
// Start collecting the data via the checkers
115+
for i := range checkers {
116+
checkers[i].Collect(ctx, *o)
117+
for _, err := range checkers[i].GetCollectionErrors() {
118+
o.Printer.Warning.Println(err)
119+
}
120+
}
121+
122+
var err error
123+
var output string
124+
switch {
125+
// If no format is specified, format and print a user-friendly output
126+
case o.Format == "" && o.GetQuery == "":
127+
clustersCounter := 0
128+
nPeers := len(o.ClustersInfo)
129+
for clusterID := range o.ClustersInfo {
130+
for i := range checkers {
131+
o.Printer.BoxSetTitle(checkers[i].GetTitle())
132+
o.Printer.BoxPrintln(checkers[i].FormatForClusterID(clusterID, *o))
133+
}
134+
135+
clustersCounter++
136+
if clustersCounter < nPeers {
137+
fmt.Printf("\n\n")
138+
}
139+
}
140+
return nil
141+
// If query specified try to retrieve the field from the output
142+
case o.GetQuery != "":
143+
data, _ := o.collectDataFromMultiClusterCheckers(checkers)
144+
145+
selectedClusterIDs := slices.Collect(maps.Keys(o.ClustersInfo))
146+
// Get the cluster selected by the query
147+
query, selectedCluster := o.getClusterFromQuery(o.GetQuery, selectedClusterIDs)
148+
149+
// Get the field from the cluster data
150+
if showData, ok := data[liqov1beta1.ClusterID(selectedCluster)]; ok {
151+
output, err = o.sPrintField(query, showData, nil)
152+
} else {
153+
err = fmt.Errorf(
154+
"cluster %q in query %q is not among the requested clusters",
155+
selectedCluster,
156+
o.GetQuery,
157+
)
158+
}
159+
default:
160+
data, _ := o.collectDataFromMultiClusterCheckers(checkers)
161+
output, err = o.sPrintMachineReadable(data)
162+
}
163+
164+
if err != nil {
165+
o.Printer.Error.Println(err)
166+
} else {
167+
fmt.Println(output)
168+
}
169+
170+
return err
100171
}

0 commit comments

Comments
 (0)