Skip to content

Commit

Permalink
fail fs-backup for windows nodes
Browse files Browse the repository at this point in the history
Signed-off-by: Lyndon-Li <[email protected]>
  • Loading branch information
Lyndon-Li committed Dec 17, 2024
1 parent 010fd1c commit 96cd224
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 2 deletions.
1 change: 1 addition & 0 deletions changelogs/unreleased/8518-Lyndon-Li
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
For issue #8424, disable fs-backup if the source/target pod is running in non-linux node
27 changes: 25 additions & 2 deletions pkg/nodeagent/node_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ import (

const (
// daemonSet is the name of the Velero node agent daemonset.
daemonSet = "node-agent"
daemonSet = "node-agent"
daemonsetWindows = "node-agent-windows"
)

var (
Expand Down Expand Up @@ -91,6 +92,18 @@ type Configs struct {

// IsRunning checks if the node agent daemonset is running properly. If not, return the error found
func IsRunning(ctx context.Context, kubeClient kubernetes.Interface, namespace string) error {
if err := isRunningOnLinux(ctx, kubeClient, namespace); err != nil {
return err
}

if err := isRunningOnWindows(ctx, kubeClient, namespace); err != nil {
return err
}

return nil
}

func isRunningOnLinux(ctx context.Context, kubeClient kubernetes.Interface, namespace string) error {
if _, err := kubeClient.AppsV1().DaemonSets(namespace).Get(ctx, daemonSet, metav1.GetOptions{}); apierrors.IsNotFound(err) {
return ErrDaemonSetNotFound
} else if err != nil {
Expand All @@ -100,6 +113,16 @@ func IsRunning(ctx context.Context, kubeClient kubernetes.Interface, namespace s
}
}

func isRunningOnWindows(ctx context.Context, kubeClient kubernetes.Interface, namespace string) error {
if _, err := kubeClient.AppsV1().DaemonSets(namespace).Get(ctx, daemonsetWindows, metav1.GetOptions{}); apierrors.IsNotFound(err) {
return ErrDaemonSetNotFound
} else if err != nil {
return err
} else {
return nil
}
}

// KbClientIsRunningInNode checks if the node agent pod is running properly in a specified node through kube client. If not, return the error found
func KbClientIsRunningInNode(ctx context.Context, namespace string, nodeName string, kubeClient kubernetes.Interface) error {
return isRunningInNode(ctx, namespace, nodeName, nil, kubeClient)
Expand All @@ -116,7 +139,7 @@ func isRunningInNode(ctx context.Context, namespace string, nodeName string, crC
}

pods := new(v1.PodList)
parsedSelector, err := labels.Parse(fmt.Sprintf("name=%s", daemonSet))
parsedSelector, err := labels.Parse(fmt.Sprintf("role=%s", daemonSet))
if err != nil {
return errors.Wrap(err, "fail to parse selector")
}
Expand Down
6 changes: 6 additions & 0 deletions pkg/podvolume/backupper.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,12 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api.
return nil, pvcSummary, nil
}

if err := kube.IsLinuxNode(b.ctx, pod.Spec.NodeName, b.crClient); err != nil {
err := errors.Wrapf(err, "Pod %s/%s is not running in linux node(%s), skip", pod.Namespace, pod.Name, pod.Spec.NodeName)
skipAllPodVolumes(pod, volumesToBackup, err, pvcSummary, log)
return nil, pvcSummary, []error{err}
}

err := nodeagent.IsRunningInNode(b.ctx, backup.Namespace, pod.Spec.NodeName, b.crClient)
if err != nil {
skipAllPodVolumes(pod, volumesToBackup, err, pvcSummary, log)
Expand Down
5 changes: 5 additions & 0 deletions pkg/podvolume/restorer.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@ func (r *restorer) RestorePodVolumes(data RestoreData, tracker *volume.RestoreVo
} else if err != nil {
r.log.WithError(err).Error("Failed to check node-agent pod status, disengage")
} else {
if err := kube.IsLinuxNode(checkCtx, nodeName, r.crClient); err != nil {
r.log.WithField("node", nodeName).WithError(err).Error("Restored pod is not running in linux node")
r.nodeAgentCheck <- errors.Wrapf(err, "restored pod %s/%s is not running in linux node %s", data.Pod.Namespace, data.Pod.Name, nodeName)
}

err = nodeagent.IsRunningInNode(checkCtx, data.Restore.Namespace, nodeName, r.crClient)
if err != nil {
r.log.WithField("node", nodeName).WithError(err).Error("node-agent pod is not running in node, abort the restore")
Expand Down
40 changes: 40 additions & 0 deletions pkg/util/kube/node.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
Copyright The Velero Contributors.
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 kube

import (
"context"

"github.com/pkg/errors"
corev1api "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)

func IsLinuxNode(ctx context.Context, nodeName string, client client.Client) error {
node := &corev1api.Node{}
if err := client.Get(ctx, types.NamespacedName{Name: nodeName}, node); err != nil {
return errors.Wrapf(err, "error getting node %s", nodeName)
}

if os, found := node.Labels["kubernetes.io/os"]; !found {
return errors.Errorf("no os type label for node %s", nodeName)
} else if os != "linux" {
return errors.Errorf("os type %s for node %s is not linux", os, nodeName)
} else {
return nil
}
}
85 changes: 85 additions & 0 deletions pkg/util/kube/node_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
Copyright The Velero Contributors.
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 kube

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"github.com/vmware-tanzu/velero/pkg/builder"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"

clientFake "sigs.k8s.io/controller-runtime/pkg/client/fake"
)

func TestIsLinuxNode(t *testing.T) {
nodeNoOSLabel := builder.ForNode("fake-node").Result()
nodeWindows := builder.ForNode("fake-node").Labels(map[string]string{"kubernetes.io/os": "windows"}).Result()
nodeLinux := builder.ForNode("fake-node").Labels(map[string]string{"kubernetes.io/os": "linux"}).Result()

scheme := runtime.NewScheme()
corev1.AddToScheme(scheme)

tests := []struct {
name string
kubeClientObj []runtime.Object
err string
}{
{
name: "error getting node",
err: "error getting node fake-node: nodes \"fake-node\" not found",
},
{
name: "no os label",
kubeClientObj: []runtime.Object{
nodeNoOSLabel,
},
err: "no os type label for node fake-node",
},
{
name: "os label does not match",
kubeClientObj: []runtime.Object{
nodeWindows,
},
err: "os type windows for node fake-node is not linux",
},
{
name: "succeed",
kubeClientObj: []runtime.Object{
nodeLinux,
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
fakeClientBuilder := clientFake.NewClientBuilder()
fakeClientBuilder = fakeClientBuilder.WithScheme(scheme)

fakeClient := fakeClientBuilder.WithRuntimeObjects(test.kubeClientObj...).Build()

err := IsLinuxNode(context.TODO(), "fake-node", fakeClient)
if err != nil {
assert.EqualError(t, err, test.err)
} else {
assert.NoError(t, err)
}
})
}
}

0 comments on commit 96cd224

Please sign in to comment.