Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelogs/unreleased/9746-priyansh17
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
refactor: generalize Velero server deployment retrieval and handling
3 changes: 1 addition & 2 deletions pkg/repository/maintenance/maintenance.go
Original file line number Diff line number Diff line change
Expand Up @@ -540,8 +540,7 @@ func buildJob(
logger logrus.FieldLogger,
) (*batchv1api.Job, error) {
// Get the Velero server deployment
deployment := &appsv1api.Deployment{}
err := cli.Get(ctx, types.NamespacedName{Name: "velero", Namespace: repo.Namespace}, deployment)
deployment, err := veleroutil.GetVeleroServerDeployment(ctx, cli, repo.Namespace)
if err != nil {
return nil, err
}
Expand Down
11 changes: 10 additions & 1 deletion pkg/repository/maintenance/maintenance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,9 @@ func TestBuildJob(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: "velero",
Namespace: "velero",
Labels: map[string]string{
"component": "velero",
},
},
Spec: appsv1api.DeploymentSpec{
Template: corev1api.PodTemplateSpec{
Expand All @@ -927,7 +930,7 @@ func TestBuildJob(t *testing.T) {
},
Containers: []corev1api.Container{
{
Name: "velero-repo-maintenance-container",
Name: "velero",
Image: "velero-image",
SecurityContext: &corev1api.SecurityContext{
RunAsNonRoot: boolptr.True(),
Expand Down Expand Up @@ -1600,6 +1603,9 @@ func TestBuildJobWithPriorityClassName(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: "velero",
Namespace: "velero",
Labels: map[string]string{
"component": "velero",
},
},
Spec: appsv1api.DeploymentSpec{
Template: corev1api.PodTemplateSpec{
Expand Down Expand Up @@ -1891,6 +1897,9 @@ func TestBuildJobWithTolerationsInheritance(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: "velero",
Namespace: "velero",
Labels: map[string]string{
"component": "velero",
},
},
Spec: appsv1api.DeploymentSpec{
Template: corev1api.PodTemplateSpec{
Expand Down
6 changes: 2 additions & 4 deletions pkg/restore/actions/pod_volume_restore_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,9 @@ import (

"github.com/pkg/errors"
"github.com/sirupsen/logrus"
appsv1api "k8s.io/api/apps/v1"
corev1api "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"

Expand Down Expand Up @@ -60,8 +58,8 @@ type PodVolumeRestoreAction struct {
}

func NewPodVolumeRestoreAction(logger logrus.FieldLogger, client corev1client.ConfigMapInterface, crClient ctrlclient.Client, namespace string) (*PodVolumeRestoreAction, error) {
deployment := &appsv1api.Deployment{}
if err := crClient.Get(context.TODO(), types.NamespacedName{Name: "velero", Namespace: namespace}, deployment); err != nil {
deployment, err := veleroutil.GetVeleroServerDeployment(context.TODO(), crClient, namespace)
if err != nil {
return nil, err
}
image := veleroutil.GetVeleroServerImage(deployment)
Expand Down
4 changes: 4 additions & 0 deletions pkg/restore/actions/pod_volume_restore_action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,12 +382,16 @@ func TestPodVolumeRestoreActionExecute(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Namespace: "velero",
Name: "velero",
Labels: map[string]string{
"component": "velero",
},
},
Spec: appsv1api.DeploymentSpec{
Template: corev1api.PodTemplateSpec{
Spec: corev1api.PodSpec{
Containers: []corev1api.Container{
{
Name: "velero",
Image: "velero/velero:v1.0",
},
},
Expand Down
64 changes: 50 additions & 14 deletions pkg/util/velero/velero.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,49 @@ limitations under the License.
package velero

import (
"context"
"fmt"

appsv1api "k8s.io/api/apps/v1"
corev1api "k8s.io/api/core/v1"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"

velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/install"
)

// GetVeleroServerDeployment finds the Velero server deployment by label selector
// instead of by hardcoded name, to support custom deployment names.
func GetVeleroServerDeployment(ctx context.Context, cli ctrlclient.Client, namespace string) (*appsv1api.Deployment, error) {
deployList := new(appsv1api.DeploymentList)
labelSelector := ctrlclient.MatchingLabels(install.Labels())
if err := cli.List(ctx, deployList, ctrlclient.InNamespace(namespace), labelSelector); err != nil {
return nil, err
}
for i := range deployList.Items {
for _, container := range deployList.Items[i].Spec.Template.Spec.Containers {
if container.Name == "velero" {
return &deployList.Items[i], nil
}
}
}
return nil, fmt.Errorf("velero server deployment not found in namespace %q", namespace)
}

// getVeleroContainer returns the container named "velero" from the deployment,
// or the first container if no container is named "velero".
func getVeleroContainer(deployment *appsv1api.Deployment) *corev1api.Container {
for i := range deployment.Spec.Template.Spec.Containers {
if deployment.Spec.Template.Spec.Containers[i].Name == "velero" {
return &deployment.Spec.Template.Spec.Containers[i]
}
}
if len(deployment.Spec.Template.Spec.Containers) > 0 {
return &deployment.Spec.Template.Spec.Containers[0]
}
return nil
}

// GetNodeSelectorFromVeleroServer get the node selector from the Velero server deployment
func GetNodeSelectorFromVeleroServer(deployment *appsv1api.Deployment) map[string]string {
return deployment.Spec.Template.Spec.NodeSelector
Expand All @@ -40,27 +77,24 @@ func GetAffinityFromVeleroServer(deployment *appsv1api.Deployment) *corev1api.Af

// GetEnvVarsFromVeleroServer get the environment variables from the Velero server deployment
func GetEnvVarsFromVeleroServer(deployment *appsv1api.Deployment) []corev1api.EnvVar {
for _, container := range deployment.Spec.Template.Spec.Containers {
// We only have one container in the Velero server deployment
return container.Env
if c := getVeleroContainer(deployment); c != nil {
return c.Env
}
return nil
}

// GetEnvFromSourcesFromVeleroServer get the environment sources from the Velero server deployment
func GetEnvFromSourcesFromVeleroServer(deployment *appsv1api.Deployment) []corev1api.EnvFromSource {
for _, container := range deployment.Spec.Template.Spec.Containers {
// We only have one container in the Velero server deployment
return container.EnvFrom
if c := getVeleroContainer(deployment); c != nil {
return c.EnvFrom
}
return nil
}

// GetVolumeMountsFromVeleroServer get the volume mounts from the Velero server deployment
func GetVolumeMountsFromVeleroServer(deployment *appsv1api.Deployment) []corev1api.VolumeMount {
for _, container := range deployment.Spec.Template.Spec.Containers {
// We only have one container in the Velero server deployment
return container.VolumeMounts
if c := getVeleroContainer(deployment); c != nil {
return c.VolumeMounts
}
return nil
}
Expand All @@ -72,9 +106,8 @@ func GetPodSecurityContextsFromVeleroServer(deployment *appsv1api.Deployment) *c

// GetContainerSecurityContextsFromVeleroServer get the security context from the Velero server deployment
func GetContainerSecurityContextsFromVeleroServer(deployment *appsv1api.Deployment) *corev1api.SecurityContext {
for _, container := range deployment.Spec.Template.Spec.Containers {
// We only have one container in the Velero server deployment
return container.SecurityContext
if c := getVeleroContainer(deployment); c != nil {
return c.SecurityContext
}
return nil
}
Expand All @@ -94,9 +127,12 @@ func GetImagePullSecretsFromVeleroServer(deployment *appsv1api.Deployment) []cor
return deployment.Spec.Template.Spec.ImagePullSecrets
}

// getVeleroServerImage get the image of the Velero server deployment
// GetVeleroServerImage get the image of the Velero server deployment
func GetVeleroServerImage(deployment *appsv1api.Deployment) string {
return deployment.Spec.Template.Spec.Containers[0].Image
if c := getVeleroContainer(deployment); c != nil {
return c.Image
}
return ""
}

// GetVeleroServerLables get the labels of the Velero server deployment
Expand Down
107 changes: 107 additions & 0 deletions pkg/util/velero/velero_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package velero

import (
"context"
"reflect"
"testing"

Expand All @@ -25,6 +26,9 @@ import (
appsv1api "k8s.io/api/apps/v1"
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
ctrlfake "sigs.k8s.io/controller-runtime/pkg/client/fake"

velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/builder"
Expand Down Expand Up @@ -924,3 +928,106 @@ func TestBSLIsAvailable(t *testing.T) {
assert.True(t, BSLIsAvailable(*availableBSL))
assert.False(t, BSLIsAvailable(*unavailableBSL))
}

func TestGetVeleroServerDeployment(t *testing.T) {
scheme := runtime.NewScheme()
require.NoError(t, appsv1api.AddToScheme(scheme))

veleroLabels := map[string]string{"component": "velero"}

matchingDeploy := &appsv1api.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "custom-velero-name",
Namespace: "velero",
Labels: veleroLabels,
},
Spec: appsv1api.DeploymentSpec{
Template: corev1api.PodTemplateSpec{
Spec: corev1api.PodSpec{
Containers: []corev1api.Container{
{Name: "velero", Image: "velero/velero:latest"},
},
},
},
},
}

nonVeleroContainerDeploy := &appsv1api.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "other",
Namespace: "velero",
Labels: veleroLabels,
},
Spec: appsv1api.DeploymentSpec{
Template: corev1api.PodTemplateSpec{
Spec: corev1api.PodSpec{
Containers: []corev1api.Container{
{Name: "other-container"},
},
},
},
},
}

unlabeledDeploy := &appsv1api.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "velero",
Namespace: "velero",
},
Spec: appsv1api.DeploymentSpec{
Template: corev1api.PodTemplateSpec{
Spec: corev1api.PodSpec{
Containers: []corev1api.Container{
{Name: "velero"},
},
},
},
},
}

tests := []struct {
name string
objects []ctrlclient.Object
namespace string
wantName string
wantErr bool
}{
{
name: "finds deployment by labels and velero container",
objects: []ctrlclient.Object{matchingDeploy},
namespace: "velero",
wantName: "custom-velero-name",
},
{
name: "skips labeled deployment without velero container",
objects: []ctrlclient.Object{nonVeleroContainerDeploy},
namespace: "velero",
wantErr: true,
},
{
name: "skips deployment without velero labels",
objects: []ctrlclient.Object{unlabeledDeploy},
namespace: "velero",
wantErr: true,
},
{
name: "no deployment in namespace",
objects: nil,
namespace: "velero",
wantErr: true,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
cli := ctrlfake.NewClientBuilder().WithScheme(scheme).WithObjects(tc.objects...).Build()
got, err := GetVeleroServerDeployment(context.Background(), cli, tc.namespace)
if tc.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tc.wantName, got.Name)
})
}
}
Loading