Skip to content

Commit

Permalink
Add liqoctl offload command
Browse files Browse the repository at this point in the history
This commit introduces the liqoctl offload namespace command to
selectively offload a namespace.
  • Loading branch information
palexster authored and adamjensenbot committed Nov 4, 2021
1 parent 529c90a commit ae47d0b
Show file tree
Hide file tree
Showing 11 changed files with 589 additions and 11 deletions.
56 changes: 56 additions & 0 deletions cmd/liqoctl/cmd/offload.go
@@ -0,0 +1,56 @@
// Copyright 2019-2021 The Liqo Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"context"

"github.com/spf13/cobra"

offloadingv1alpha1 "github.com/liqotech/liqo/apis/offloading/v1alpha1"
"github.com/liqotech/liqo/pkg/liqoctl/offload"
"github.com/liqotech/liqo/pkg/utils/args"
)

func newOffloadCommand(ctx context.Context) *cobra.Command {
var offloadClusterCmd = &cobra.Command{
Use: offload.UseCommand,
SilenceUsage: true,
Short: offload.LiqoctlOffloadShortHelp,
Long: offload.LiqoctlOffloadLongHelp,
Args: cobra.MinimumNArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
return offload.HandleOffloadCommand(ctx, cmd, args)
},
}
podOffloadingStrategy := args.NewEnum([]string{string(offloadingv1alpha1.LocalAndRemotePodOffloadingStrategyType),
string(offloadingv1alpha1.RemotePodOffloadingStrategyType),
string(offloadingv1alpha1.LocalPodOffloadingStrategyType)},
string(offloadingv1alpha1.LocalAndRemotePodOffloadingStrategyType))

offloadClusterCmd.PersistentFlags().Var(podOffloadingStrategy,
offload.PodOffloadingStrategyFlag, offload.PodOffloadingStrategyHelp)
namespaceMappingStrategy := args.NewEnum([]string{string(offloadingv1alpha1.EnforceSameNameMappingStrategyType),
string(offloadingv1alpha1.DefaultNameMappingStrategyType)},
string(offloadingv1alpha1.DefaultNameMappingStrategyType))

offloadClusterCmd.PersistentFlags().Var(namespaceMappingStrategy, offload.NamespaceMappingStrategyFlag,
offload.NamespaceMappingStrategyHelp)
offloadClusterCmd.PersistentFlags().String(offload.AcceptedLabelsFlag,
offload.AcceptedLabelsDefault, offload.AcceptedLabelsHelp)
offloadClusterCmd.PersistentFlags().String(offload.DeniedLabelsFlag,
offload.DeniedLabelDefault, offload.DeniedLabelsHelp)
return offloadClusterCmd
}
1 change: 1 addition & 0 deletions cmd/liqoctl/cmd/root.go
Expand Up @@ -55,5 +55,6 @@ func NewRootCommand(ctx context.Context) *cobra.Command {
rootCmd.AddCommand(newDocsCommand(ctx))
rootCmd.AddCommand(newVersionCommand())
rootCmd.AddCommand(newStatusCommand(ctx))
rootCmd.AddCommand(newOffloadCommand(ctx))
return rootCmd
}
8 changes: 6 additions & 2 deletions cmd/liqoctl/main.go
Expand Up @@ -21,11 +21,11 @@ import (
"syscall"
"time"

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

discoveryv1alpha1 "github.com/liqotech/liqo/apis/discovery/v1alpha1"
offloadingv1alpha1 "github.com/liqotech/liqo/apis/offloading/v1alpha1"
liqocmd "github.com/liqotech/liqo/cmd/liqoctl/cmd"
)

Expand All @@ -35,6 +35,7 @@ const (

func init() {
_ = discoveryv1alpha1.AddToScheme(scheme.Scheme)
_ = offloadingv1alpha1.AddToScheme(scheme.Scheme)
}

func main() {
Expand All @@ -47,5 +48,8 @@ func main() {
}()

cmd := liqocmd.NewRootCommand(ctx)
cobra.CheckErr(cmd.ExecuteContext(ctx))
msg := cmd.ExecuteContext(ctx)
if msg != nil {
os.Exit(1)
}
}
4 changes: 2 additions & 2 deletions pkg/liqoctl/add/handler.go
Expand Up @@ -29,7 +29,7 @@ import (
discoveryv1alpha1 "github.com/liqotech/liqo/apis/discovery/v1alpha1"
"github.com/liqotech/liqo/pkg/discovery"
"github.com/liqotech/liqo/pkg/liqoctl/common"
utils2 "github.com/liqotech/liqo/pkg/utils"
"github.com/liqotech/liqo/pkg/utils"
authenticationtokenutils "github.com/liqotech/liqo/pkg/utils/authenticationtoken"
foreigncluster "github.com/liqotech/liqo/pkg/utils/foreignCluster"
)
Expand Down Expand Up @@ -89,7 +89,7 @@ func processAddCluster(ctx context.Context, t *ClusterArgs, clientSet kubernetes
return err
}

clusterID, err := utils2.GetClusterIDWithControllerClient(ctx, k8sClient, t.Namespace)
clusterID, err := utils.GetClusterIDWithControllerClient(ctx, k8sClient, t.Namespace)
if err != nil {
return err
}
Expand Down
17 changes: 10 additions & 7 deletions pkg/liqoctl/generate/handler.go
Expand Up @@ -45,7 +45,10 @@ func HandleGenerateAddCommand(ctx context.Context, liqoNamespace string, printOn
return err
}

commandString := processGenerateCommand(ctx, clientSet, liqoNamespace, commandName)
commandString, err := processGenerateCommand(ctx, clientSet, liqoNamespace, commandName)
if err != nil {
return err
}

if printOnlyCommand {
fmt.Println(commandString)
Expand All @@ -56,21 +59,21 @@ func HandleGenerateAddCommand(ctx context.Context, liqoNamespace string, printOn
return nil
}

func processGenerateCommand(ctx context.Context, clientSet client.Client, liqoNamespace, commandName string) string {
func processGenerateCommand(ctx context.Context, clientSet client.Client, liqoNamespace, commandName string) (string, error) {
localToken, err := auth.GetToken(ctx, clientSet, liqoNamespace)
if err != nil {
klog.Fatalf(err.Error())
return "", err
}

clusterID, err := utils.GetClusterIDWithControllerClient(ctx, clientSet, liqoNamespace)
if err != nil {
klog.Fatalf(err.Error())
return "", err
}

// Retrieve the liqo controller manager deployment args
args, err := RetrieveLiqoControllerManagerDeploymentArgs(ctx, clientSet, liqoNamespace)
if err != nil {
klog.Fatalf(err.Error())
return "", err
}

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

func generateCommandString(commandName, authEP, clusterID, localToken, clusterName string) string {
Expand Down
59 changes: 59 additions & 0 deletions pkg/liqoctl/offload/consts.go
@@ -0,0 +1,59 @@
// Copyright 2019-2021 The Liqo Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package offload

const (
// LiqoctlOffloadShortHelp contains the short help string for liqoctl Offload command.
LiqoctlOffloadShortHelp = "Offload a namespace to remote clusters"
// LiqoctlOffloadLongHelp contains the Long help string for liqoctl Offload command.
LiqoctlOffloadLongHelp = `Offload a namespace to remote clusters with the default values:
$ liqoctl Offload cluster my-cluster
To just enable service reflection, offload a namespace with the EnforceSameName namespace mapping strategy and
and a Local --pod-offloading-strategy.
$ liqoctl offload namespace liqo-demo --namespace-mapping-strategy=EnforceSameName --pod-offloading-strategy=Local
`
// UseCommand contains the verb of the Offload command.
UseCommand = "offload"
// ClusterResourceName contains the name of the resource offloaded in liqoctl Offload.
ClusterResourceName = "namespace"
// SuccessfulMessage is printed when a Offload cluster command has scucceded.
SuccessfulMessage = `
Success 👌!
`
// PodOffloadingStrategyFlag specifies the pod offloading strategy flag name.
PodOffloadingStrategyFlag = "pod-offloading-strategy"
// PodOffloadingStrategyHelp specifies the help message for the PodOffloadingStrategy flag.
PodOffloadingStrategyHelp = "Select the desired pod offloading strategy"
// NamespaceMappingStrategyFlag specifies the namespace mapping flag name.
NamespaceMappingStrategyFlag = "namespace-mapping-strategy"
// NamespaceMappingStrategyHelp specifies the help message for the NamespaceMappingStrategy flag.
NamespaceMappingStrategyHelp = "Select the desired pod offloading strategy"

// AcceptedLabelsFlag specifies the accepted labels flag name.
AcceptedLabelsFlag = "accepted-cluster-labels"
// AcceptedLabelsDefault specifies the accepted default labels used to forge the clusterSelector field.
AcceptedLabelsDefault = "liqo.io/type=virtual-node"
// AcceptedLabelsHelp specifies the help message for the AcceptedLabels flag.
AcceptedLabelsHelp = "The set of labels accepted as valid clusterLabels for the clusterSelector field"
// DeniedLabelsFlag specifies the accepted labels flag name.
DeniedLabelsFlag = "denied-cluster-labels"
// DeniedLabelDefault specifies the denied default labels used to forge the clusterSelector field.
DeniedLabelDefault = ""
// DeniedLabelsHelp specifies the help message for the DeniedLabels flag.
DeniedLabelsHelp = "The set of labels identified as forbidden clusterLabels for the clusterSelector field"
)
16 changes: 16 additions & 0 deletions pkg/liqoctl/offload/doc.go
@@ -0,0 +1,16 @@
// Copyright 2019-2021 The Liqo Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package offload includes the logic for the `liqoctl offload` command
package offload
137 changes: 137 additions & 0 deletions pkg/liqoctl/offload/namespace.go
@@ -0,0 +1,137 @@
// Copyright 2019-2021 The Liqo Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package offload

import (
"context"

"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

offloadingv1alpha1 "github.com/liqotech/liqo/apis/offloading/v1alpha1"
"github.com/liqotech/liqo/pkg/consts"
"github.com/liqotech/liqo/pkg/liqoctl/common"
argsutils "github.com/liqotech/liqo/pkg/utils/args"
logsutils "github.com/liqotech/liqo/pkg/utils/logs"
)

// HandleOffloadCommand implements the "offload namespace" command.
// It forges and createOrUpdate a namespaceOffloading resource for a given namespace according to the flag values.
func HandleOffloadCommand(ctx context.Context, command *cobra.Command, args []string) error {
if !klog.V(4).Enabled() {
klog.SetLogFilter(logsutils.LogFilter{})
}

config, err := common.GetLiqoctlRestConf()
if err != nil {
return err
}

k8sClient, err := client.New(config, client.Options{})
if err != nil {
return err
}

var acceptedClusterLabels argsutils.StringMap
if err := acceptedClusterLabels.Set(command.Flag(AcceptedLabelsFlag).Value.String()); err != nil {
return err
}
var deniedClusterLabels argsutils.StringMap
if err := deniedClusterLabels.Set(command.Flag(DeniedLabelsFlag).Value.String()); err != nil {
return err
}

nsOffloading := forgeNamespaceOffloading(command, args, acceptedClusterLabels, deniedClusterLabels)

_, err = controllerutil.CreateOrUpdate(ctx, k8sClient, nsOffloading, func() error {
nsOffloading.Spec.PodOffloadingStrategy = forgePodOffloadingStrategy(command)
nsOffloading.Spec.NamespaceMappingStrategy = forgeNamespaceMappingStrategy(command)
nsOffloading.Spec.ClusterSelector = forgeClusterSelector(acceptedClusterLabels, deniedClusterLabels)
return nil
})
if err != nil {
return err
}

return nil
}

func forgeNamespaceOffloading(command *cobra.Command, args []string,
acceptedLabels, deniedLabels argsutils.StringMap) *offloadingv1alpha1.NamespaceOffloading {
return &offloadingv1alpha1.NamespaceOffloading{
ObjectMeta: metav1.ObjectMeta{
Name: consts.DefaultNamespaceOffloadingName,
Namespace: args[1],
},
Spec: offloadingv1alpha1.NamespaceOffloadingSpec{
NamespaceMappingStrategy: forgeNamespaceMappingStrategy(command),
PodOffloadingStrategy: forgePodOffloadingStrategy(command),
ClusterSelector: forgeClusterSelector(acceptedLabels, deniedLabels),
},
}
}

func forgeClusterSelector(acceptedLabels, deniedLabels argsutils.StringMap) corev1.NodeSelector {
var l []corev1.NodeSelectorTerm

// No acceptedLabels are defined, backing to the default behavior
if len(acceptedLabels.StringMap) == 0 && len(deniedLabels.StringMap) == 0 {
return corev1.NodeSelector{
NodeSelectorTerms: []corev1.NodeSelectorTerm{{
MatchExpressions: []corev1.NodeSelectorRequirement{{
Key: consts.TypeLabel,
Operator: corev1.NodeSelectorOpIn,
Values: []string{consts.TypeNode},
}}},
},
}
}

for k, v := range acceptedLabels.StringMap {
l = append(l, corev1.NodeSelectorTerm{
MatchExpressions: []corev1.NodeSelectorRequirement{{
Key: k,
Operator: corev1.NodeSelectorOpIn,
Values: []string{v},
},
},
})
}

for k, v := range deniedLabels.StringMap {
l = append(l, corev1.NodeSelectorTerm{
MatchExpressions: []corev1.NodeSelectorRequirement{{
Key: k,
Operator: corev1.NodeSelectorOpNotIn,
Values: []string{v},
},
},
})
}

return corev1.NodeSelector{NodeSelectorTerms: l}
}

func forgePodOffloadingStrategy(command *cobra.Command) offloadingv1alpha1.PodOffloadingStrategyType {
return offloadingv1alpha1.PodOffloadingStrategyType(command.Flag(PodOffloadingStrategyFlag).Value.String())
}

func forgeNamespaceMappingStrategy(command *cobra.Command) offloadingv1alpha1.NamespaceMappingStrategyType {
return offloadingv1alpha1.NamespaceMappingStrategyType(command.Flag(NamespaceMappingStrategyFlag).Value.String())
}

0 comments on commit ae47d0b

Please sign in to comment.