Skip to content

Commit ae47d0b

Browse files
palexsteradamjensenbot
authored andcommitted
Add liqoctl offload command
This commit introduces the liqoctl offload namespace command to selectively offload a namespace.
1 parent 529c90a commit ae47d0b

File tree

11 files changed

+589
-11
lines changed

11 files changed

+589
-11
lines changed

cmd/liqoctl/cmd/offload.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2019-2021 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+
package cmd
16+
17+
import (
18+
"context"
19+
20+
"github.com/spf13/cobra"
21+
22+
offloadingv1alpha1 "github.com/liqotech/liqo/apis/offloading/v1alpha1"
23+
"github.com/liqotech/liqo/pkg/liqoctl/offload"
24+
"github.com/liqotech/liqo/pkg/utils/args"
25+
)
26+
27+
func newOffloadCommand(ctx context.Context) *cobra.Command {
28+
var offloadClusterCmd = &cobra.Command{
29+
Use: offload.UseCommand,
30+
SilenceUsage: true,
31+
Short: offload.LiqoctlOffloadShortHelp,
32+
Long: offload.LiqoctlOffloadLongHelp,
33+
Args: cobra.MinimumNArgs(2),
34+
RunE: func(cmd *cobra.Command, args []string) error {
35+
return offload.HandleOffloadCommand(ctx, cmd, args)
36+
},
37+
}
38+
podOffloadingStrategy := args.NewEnum([]string{string(offloadingv1alpha1.LocalAndRemotePodOffloadingStrategyType),
39+
string(offloadingv1alpha1.RemotePodOffloadingStrategyType),
40+
string(offloadingv1alpha1.LocalPodOffloadingStrategyType)},
41+
string(offloadingv1alpha1.LocalAndRemotePodOffloadingStrategyType))
42+
43+
offloadClusterCmd.PersistentFlags().Var(podOffloadingStrategy,
44+
offload.PodOffloadingStrategyFlag, offload.PodOffloadingStrategyHelp)
45+
namespaceMappingStrategy := args.NewEnum([]string{string(offloadingv1alpha1.EnforceSameNameMappingStrategyType),
46+
string(offloadingv1alpha1.DefaultNameMappingStrategyType)},
47+
string(offloadingv1alpha1.DefaultNameMappingStrategyType))
48+
49+
offloadClusterCmd.PersistentFlags().Var(namespaceMappingStrategy, offload.NamespaceMappingStrategyFlag,
50+
offload.NamespaceMappingStrategyHelp)
51+
offloadClusterCmd.PersistentFlags().String(offload.AcceptedLabelsFlag,
52+
offload.AcceptedLabelsDefault, offload.AcceptedLabelsHelp)
53+
offloadClusterCmd.PersistentFlags().String(offload.DeniedLabelsFlag,
54+
offload.DeniedLabelDefault, offload.DeniedLabelsHelp)
55+
return offloadClusterCmd
56+
}

cmd/liqoctl/cmd/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,6 @@ func NewRootCommand(ctx context.Context) *cobra.Command {
5555
rootCmd.AddCommand(newDocsCommand(ctx))
5656
rootCmd.AddCommand(newVersionCommand())
5757
rootCmd.AddCommand(newStatusCommand(ctx))
58+
rootCmd.AddCommand(newOffloadCommand(ctx))
5859
return rootCmd
5960
}

cmd/liqoctl/main.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ import (
2121
"syscall"
2222
"time"
2323

24-
"github.com/spf13/cobra"
2524
"k8s.io/client-go/kubernetes/scheme"
2625
_ "k8s.io/client-go/plugin/pkg/client/auth"
2726

2827
discoveryv1alpha1 "github.com/liqotech/liqo/apis/discovery/v1alpha1"
28+
offloadingv1alpha1 "github.com/liqotech/liqo/apis/offloading/v1alpha1"
2929
liqocmd "github.com/liqotech/liqo/cmd/liqoctl/cmd"
3030
)
3131

@@ -35,6 +35,7 @@ const (
3535

3636
func init() {
3737
_ = discoveryv1alpha1.AddToScheme(scheme.Scheme)
38+
_ = offloadingv1alpha1.AddToScheme(scheme.Scheme)
3839
}
3940

4041
func main() {
@@ -47,5 +48,8 @@ func main() {
4748
}()
4849

4950
cmd := liqocmd.NewRootCommand(ctx)
50-
cobra.CheckErr(cmd.ExecuteContext(ctx))
51+
msg := cmd.ExecuteContext(ctx)
52+
if msg != nil {
53+
os.Exit(1)
54+
}
5155
}

pkg/liqoctl/add/handler.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import (
2929
discoveryv1alpha1 "github.com/liqotech/liqo/apis/discovery/v1alpha1"
3030
"github.com/liqotech/liqo/pkg/discovery"
3131
"github.com/liqotech/liqo/pkg/liqoctl/common"
32-
utils2 "github.com/liqotech/liqo/pkg/utils"
32+
"github.com/liqotech/liqo/pkg/utils"
3333
authenticationtokenutils "github.com/liqotech/liqo/pkg/utils/authenticationtoken"
3434
foreigncluster "github.com/liqotech/liqo/pkg/utils/foreignCluster"
3535
)
@@ -89,7 +89,7 @@ func processAddCluster(ctx context.Context, t *ClusterArgs, clientSet kubernetes
8989
return err
9090
}
9191

92-
clusterID, err := utils2.GetClusterIDWithControllerClient(ctx, k8sClient, t.Namespace)
92+
clusterID, err := utils.GetClusterIDWithControllerClient(ctx, k8sClient, t.Namespace)
9393
if err != nil {
9494
return err
9595
}

pkg/liqoctl/generate/handler.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ func HandleGenerateAddCommand(ctx context.Context, liqoNamespace string, printOn
4545
return err
4646
}
4747

48-
commandString := processGenerateCommand(ctx, clientSet, liqoNamespace, commandName)
48+
commandString, err := processGenerateCommand(ctx, clientSet, liqoNamespace, commandName)
49+
if err != nil {
50+
return err
51+
}
4952

5053
if printOnlyCommand {
5154
fmt.Println(commandString)
@@ -56,21 +59,21 @@ func HandleGenerateAddCommand(ctx context.Context, liqoNamespace string, printOn
5659
return nil
5760
}
5861

59-
func processGenerateCommand(ctx context.Context, clientSet client.Client, liqoNamespace, commandName string) string {
62+
func processGenerateCommand(ctx context.Context, clientSet client.Client, liqoNamespace, commandName string) (string, error) {
6063
localToken, err := auth.GetToken(ctx, clientSet, liqoNamespace)
6164
if err != nil {
62-
klog.Fatalf(err.Error())
65+
return "", err
6366
}
6467

6568
clusterID, err := utils.GetClusterIDWithControllerClient(ctx, clientSet, liqoNamespace)
6669
if err != nil {
67-
klog.Fatalf(err.Error())
70+
return "", err
6871
}
6972

7073
// Retrieve the liqo controller manager deployment args
7174
args, err := RetrieveLiqoControllerManagerDeploymentArgs(ctx, clientSet, liqoNamespace)
7275
if err != nil {
73-
klog.Fatalf(err.Error())
76+
return "", err
7477
}
7578

7679
// The error is discarded, since an empty string is returned in case the key is not found, which is fine.
@@ -81,9 +84,9 @@ func processGenerateCommand(ctx context.Context, clientSet client.Client, liqoNa
8184
authEP, err := foreigncluster.GetHomeAuthURL(ctx, clientSet,
8285
authServiceAddressOverride, authServicePortOverride, liqoNamespace)
8386
if err != nil {
84-
klog.Fatalf("an error occurred while retrieving the liqo-auth service: %s", err)
87+
klog.Fatalf(err.Error())
8588
}
86-
return generateCommandString(commandName, authEP, clusterID, localToken, clusterName)
89+
return generateCommandString(commandName, authEP, clusterID, localToken, clusterName), nil
8790
}
8891

8992
func generateCommandString(commandName, authEP, clusterID, localToken, clusterName string) string {

pkg/liqoctl/offload/consts.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2019-2021 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+
package offload
16+
17+
const (
18+
// LiqoctlOffloadShortHelp contains the short help string for liqoctl Offload command.
19+
LiqoctlOffloadShortHelp = "Offload a namespace to remote clusters"
20+
// LiqoctlOffloadLongHelp contains the Long help string for liqoctl Offload command.
21+
LiqoctlOffloadLongHelp = `Offload a namespace to remote clusters with the default values:
22+
23+
$ liqoctl Offload cluster my-cluster
24+
25+
To just enable service reflection, offload a namespace with the EnforceSameName namespace mapping strategy and
26+
and a Local --pod-offloading-strategy.
27+
28+
$ liqoctl offload namespace liqo-demo --namespace-mapping-strategy=EnforceSameName --pod-offloading-strategy=Local
29+
`
30+
// UseCommand contains the verb of the Offload command.
31+
UseCommand = "offload"
32+
// ClusterResourceName contains the name of the resource offloaded in liqoctl Offload.
33+
ClusterResourceName = "namespace"
34+
// SuccessfulMessage is printed when a Offload cluster command has scucceded.
35+
SuccessfulMessage = `
36+
Success 👌!
37+
`
38+
// PodOffloadingStrategyFlag specifies the pod offloading strategy flag name.
39+
PodOffloadingStrategyFlag = "pod-offloading-strategy"
40+
// PodOffloadingStrategyHelp specifies the help message for the PodOffloadingStrategy flag.
41+
PodOffloadingStrategyHelp = "Select the desired pod offloading strategy"
42+
// NamespaceMappingStrategyFlag specifies the namespace mapping flag name.
43+
NamespaceMappingStrategyFlag = "namespace-mapping-strategy"
44+
// NamespaceMappingStrategyHelp specifies the help message for the NamespaceMappingStrategy flag.
45+
NamespaceMappingStrategyHelp = "Select the desired pod offloading strategy"
46+
47+
// AcceptedLabelsFlag specifies the accepted labels flag name.
48+
AcceptedLabelsFlag = "accepted-cluster-labels"
49+
// AcceptedLabelsDefault specifies the accepted default labels used to forge the clusterSelector field.
50+
AcceptedLabelsDefault = "liqo.io/type=virtual-node"
51+
// AcceptedLabelsHelp specifies the help message for the AcceptedLabels flag.
52+
AcceptedLabelsHelp = "The set of labels accepted as valid clusterLabels for the clusterSelector field"
53+
// DeniedLabelsFlag specifies the accepted labels flag name.
54+
DeniedLabelsFlag = "denied-cluster-labels"
55+
// DeniedLabelDefault specifies the denied default labels used to forge the clusterSelector field.
56+
DeniedLabelDefault = ""
57+
// DeniedLabelsHelp specifies the help message for the DeniedLabels flag.
58+
DeniedLabelsHelp = "The set of labels identified as forbidden clusterLabels for the clusterSelector field"
59+
)

pkg/liqoctl/offload/doc.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2019-2021 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+
// Package offload includes the logic for the `liqoctl offload` command
16+
package offload

pkg/liqoctl/offload/namespace.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// Copyright 2019-2021 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+
package offload
16+
17+
import (
18+
"context"
19+
20+
"github.com/spf13/cobra"
21+
corev1 "k8s.io/api/core/v1"
22+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23+
"k8s.io/klog/v2"
24+
"sigs.k8s.io/controller-runtime/pkg/client"
25+
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
26+
27+
offloadingv1alpha1 "github.com/liqotech/liqo/apis/offloading/v1alpha1"
28+
"github.com/liqotech/liqo/pkg/consts"
29+
"github.com/liqotech/liqo/pkg/liqoctl/common"
30+
argsutils "github.com/liqotech/liqo/pkg/utils/args"
31+
logsutils "github.com/liqotech/liqo/pkg/utils/logs"
32+
)
33+
34+
// HandleOffloadCommand implements the "offload namespace" command.
35+
// It forges and createOrUpdate a namespaceOffloading resource for a given namespace according to the flag values.
36+
func HandleOffloadCommand(ctx context.Context, command *cobra.Command, args []string) error {
37+
if !klog.V(4).Enabled() {
38+
klog.SetLogFilter(logsutils.LogFilter{})
39+
}
40+
41+
config, err := common.GetLiqoctlRestConf()
42+
if err != nil {
43+
return err
44+
}
45+
46+
k8sClient, err := client.New(config, client.Options{})
47+
if err != nil {
48+
return err
49+
}
50+
51+
var acceptedClusterLabels argsutils.StringMap
52+
if err := acceptedClusterLabels.Set(command.Flag(AcceptedLabelsFlag).Value.String()); err != nil {
53+
return err
54+
}
55+
var deniedClusterLabels argsutils.StringMap
56+
if err := deniedClusterLabels.Set(command.Flag(DeniedLabelsFlag).Value.String()); err != nil {
57+
return err
58+
}
59+
60+
nsOffloading := forgeNamespaceOffloading(command, args, acceptedClusterLabels, deniedClusterLabels)
61+
62+
_, err = controllerutil.CreateOrUpdate(ctx, k8sClient, nsOffloading, func() error {
63+
nsOffloading.Spec.PodOffloadingStrategy = forgePodOffloadingStrategy(command)
64+
nsOffloading.Spec.NamespaceMappingStrategy = forgeNamespaceMappingStrategy(command)
65+
nsOffloading.Spec.ClusterSelector = forgeClusterSelector(acceptedClusterLabels, deniedClusterLabels)
66+
return nil
67+
})
68+
if err != nil {
69+
return err
70+
}
71+
72+
return nil
73+
}
74+
75+
func forgeNamespaceOffloading(command *cobra.Command, args []string,
76+
acceptedLabels, deniedLabels argsutils.StringMap) *offloadingv1alpha1.NamespaceOffloading {
77+
return &offloadingv1alpha1.NamespaceOffloading{
78+
ObjectMeta: metav1.ObjectMeta{
79+
Name: consts.DefaultNamespaceOffloadingName,
80+
Namespace: args[1],
81+
},
82+
Spec: offloadingv1alpha1.NamespaceOffloadingSpec{
83+
NamespaceMappingStrategy: forgeNamespaceMappingStrategy(command),
84+
PodOffloadingStrategy: forgePodOffloadingStrategy(command),
85+
ClusterSelector: forgeClusterSelector(acceptedLabels, deniedLabels),
86+
},
87+
}
88+
}
89+
90+
func forgeClusterSelector(acceptedLabels, deniedLabels argsutils.StringMap) corev1.NodeSelector {
91+
var l []corev1.NodeSelectorTerm
92+
93+
// No acceptedLabels are defined, backing to the default behavior
94+
if len(acceptedLabels.StringMap) == 0 && len(deniedLabels.StringMap) == 0 {
95+
return corev1.NodeSelector{
96+
NodeSelectorTerms: []corev1.NodeSelectorTerm{{
97+
MatchExpressions: []corev1.NodeSelectorRequirement{{
98+
Key: consts.TypeLabel,
99+
Operator: corev1.NodeSelectorOpIn,
100+
Values: []string{consts.TypeNode},
101+
}}},
102+
},
103+
}
104+
}
105+
106+
for k, v := range acceptedLabels.StringMap {
107+
l = append(l, corev1.NodeSelectorTerm{
108+
MatchExpressions: []corev1.NodeSelectorRequirement{{
109+
Key: k,
110+
Operator: corev1.NodeSelectorOpIn,
111+
Values: []string{v},
112+
},
113+
},
114+
})
115+
}
116+
117+
for k, v := range deniedLabels.StringMap {
118+
l = append(l, corev1.NodeSelectorTerm{
119+
MatchExpressions: []corev1.NodeSelectorRequirement{{
120+
Key: k,
121+
Operator: corev1.NodeSelectorOpNotIn,
122+
Values: []string{v},
123+
},
124+
},
125+
})
126+
}
127+
128+
return corev1.NodeSelector{NodeSelectorTerms: l}
129+
}
130+
131+
func forgePodOffloadingStrategy(command *cobra.Command) offloadingv1alpha1.PodOffloadingStrategyType {
132+
return offloadingv1alpha1.PodOffloadingStrategyType(command.Flag(PodOffloadingStrategyFlag).Value.String())
133+
}
134+
135+
func forgeNamespaceMappingStrategy(command *cobra.Command) offloadingv1alpha1.NamespaceMappingStrategyType {
136+
return offloadingv1alpha1.NamespaceMappingStrategyType(command.Flag(NamespaceMappingStrategyFlag).Value.String())
137+
}

0 commit comments

Comments
 (0)