mirror of
https://github.com/vmware-tanzu/velero.git
synced 2025-12-23 06:15:21 +00:00
405 lines
12 KiB
Go
405 lines
12 KiB
Go
package install
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
appsv1api "k8s.io/api/apps/v1"
|
|
corev1api "k8s.io/api/core/v1"
|
|
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
|
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
|
|
|
v1crds "github.com/vmware-tanzu/velero/config/crd/v1/crds"
|
|
"github.com/vmware-tanzu/velero/pkg/test"
|
|
)
|
|
|
|
func TestInstall(t *testing.T) {
|
|
dc := &test.FakeDynamicClient{}
|
|
dc.On("Create", mock.Anything).Return(&unstructured.Unstructured{}, nil)
|
|
|
|
factory := &test.FakeDynamicFactory{}
|
|
factory.On("ClientForGroupVersionResource", mock.Anything, mock.Anything, mock.Anything).Return(dc, nil)
|
|
|
|
c := fake.NewClientBuilder().WithObjects(
|
|
&apiextv1.CustomResourceDefinition{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "backuprepositories.velero.io",
|
|
},
|
|
|
|
Status: apiextv1.CustomResourceDefinitionStatus{
|
|
Conditions: []apiextv1.CustomResourceDefinitionCondition{
|
|
{
|
|
Type: apiextv1.Established,
|
|
Status: apiextv1.ConditionTrue,
|
|
},
|
|
{
|
|
Type: apiextv1.NamesAccepted,
|
|
Status: apiextv1.ConditionTrue,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
).Build()
|
|
|
|
resources := &unstructured.UnstructuredList{}
|
|
require.NoError(t, appendUnstructured(resources, v1crds.CRDs[0]))
|
|
require.NoError(t, appendUnstructured(resources, Namespace("velero")))
|
|
|
|
assert.NoError(t, Install(factory, c, resources, os.Stdout, false))
|
|
}
|
|
|
|
func Test_crdsAreReady(t *testing.T) {
|
|
c := fake.NewClientBuilder().WithObjects(
|
|
&apiextv1beta1.CustomResourceDefinition{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "backuprepositories.velero.io",
|
|
},
|
|
|
|
Status: apiextv1beta1.CustomResourceDefinitionStatus{
|
|
Conditions: []apiextv1beta1.CustomResourceDefinitionCondition{
|
|
{
|
|
Type: apiextv1beta1.Established,
|
|
Status: apiextv1beta1.ConditionTrue,
|
|
},
|
|
{
|
|
Type: apiextv1beta1.NamesAccepted,
|
|
Status: apiextv1beta1.ConditionTrue,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
).Build()
|
|
|
|
crd := &apiextv1beta1.CustomResourceDefinition{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "CustomResourceDefinition",
|
|
APIVersion: "v1beta1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "backuprepositories.velero.io",
|
|
},
|
|
}
|
|
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(crd)
|
|
require.NoError(t, err)
|
|
|
|
crds := []*unstructured.Unstructured{
|
|
{
|
|
Object: obj,
|
|
},
|
|
}
|
|
|
|
ready, err := crdsAreReady(c, crds)
|
|
require.NoError(t, err)
|
|
assert.True(t, ready)
|
|
}
|
|
|
|
func TestDeploymentIsReady(t *testing.T) {
|
|
deployment := &appsv1api.Deployment{
|
|
Status: appsv1api.DeploymentStatus{
|
|
Conditions: []appsv1api.DeploymentCondition{
|
|
{
|
|
Type: appsv1api.DeploymentAvailable,
|
|
Status: corev1api.ConditionTrue,
|
|
LastTransitionTime: metav1.NewTime(time.Now().Add(-15 * time.Second)),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(deployment)
|
|
require.NoError(t, err)
|
|
|
|
dc := &test.FakeDynamicClient{}
|
|
dc.On("Get", mock.Anything, mock.Anything).Return(&unstructured.Unstructured{Object: obj}, nil)
|
|
|
|
factory := &test.FakeDynamicFactory{}
|
|
factory.On("ClientForGroupVersionResource", mock.Anything, mock.Anything, mock.Anything).Return(dc, nil)
|
|
|
|
ready, err := DeploymentIsReady(factory, "velero")
|
|
require.NoError(t, err)
|
|
assert.True(t, ready)
|
|
}
|
|
|
|
func TestNodeAgentIsReady(t *testing.T) {
|
|
daemonset := &appsv1api.DaemonSet{
|
|
Status: appsv1api.DaemonSetStatus{
|
|
NumberAvailable: 1,
|
|
DesiredNumberScheduled: 1,
|
|
},
|
|
}
|
|
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(daemonset)
|
|
require.NoError(t, err)
|
|
|
|
dc := &test.FakeDynamicClient{}
|
|
dc.On("Get", mock.Anything, mock.Anything).Return(&unstructured.Unstructured{Object: obj}, nil)
|
|
|
|
factory := &test.FakeDynamicFactory{}
|
|
factory.On("ClientForGroupVersionResource", mock.Anything, mock.Anything, mock.Anything).Return(dc, nil)
|
|
|
|
ready, err := NodeAgentIsReady(factory, "velero")
|
|
require.NoError(t, err)
|
|
assert.True(t, ready)
|
|
}
|
|
|
|
func TestNodeAgentWindowsIsReady(t *testing.T) {
|
|
daemonset := &appsv1api.DaemonSet{
|
|
Status: appsv1api.DaemonSetStatus{
|
|
NumberAvailable: 0,
|
|
DesiredNumberScheduled: 0,
|
|
},
|
|
}
|
|
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(daemonset)
|
|
require.NoError(t, err)
|
|
|
|
dc := &test.FakeDynamicClient{}
|
|
dc.On("Get", mock.Anything, mock.Anything).Return(&unstructured.Unstructured{Object: obj}, nil)
|
|
|
|
factory := &test.FakeDynamicFactory{}
|
|
factory.On("ClientForGroupVersionResource", mock.Anything, mock.Anything, mock.Anything).Return(dc, nil)
|
|
|
|
ready, err := NodeAgentWindowsIsReady(factory, "velero")
|
|
require.NoError(t, err)
|
|
assert.True(t, ready)
|
|
}
|
|
|
|
func TestCreateOrApplyResourceError(t *testing.T) {
|
|
r := &unstructured.Unstructured{
|
|
Object: map[string]any{
|
|
"apiVersion": "v1",
|
|
"kind": "ConfigMap",
|
|
"metadata": map[string]any{
|
|
"name": "test-configmap",
|
|
"namespace": "velero",
|
|
},
|
|
},
|
|
}
|
|
|
|
dc := &test.FakeDynamicClient{}
|
|
expectedErr := errors.New("create error")
|
|
dc.On("Create", mock.Anything).Return(&unstructured.Unstructured{}, expectedErr)
|
|
|
|
factory := &test.FakeDynamicFactory{}
|
|
factory.On("ClientForGroupVersionResource", mock.Anything, mock.Anything, mock.Anything).Return(dc, nil)
|
|
|
|
var buf bytes.Buffer
|
|
err := createOrApplyResource(r, factory, &buf, false)
|
|
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), expectedErr.Error())
|
|
}
|
|
|
|
func TestCreateOrApplyResourceAlreadyExists(t *testing.T) {
|
|
r := &unstructured.Unstructured{
|
|
Object: map[string]any{
|
|
"apiVersion": "v1",
|
|
"kind": "ConfigMap",
|
|
"metadata": map[string]any{
|
|
"name": "test-configmap",
|
|
"namespace": "velero",
|
|
},
|
|
},
|
|
}
|
|
|
|
dc := &test.FakeDynamicClient{}
|
|
alreadyExistsErr := apierrors.NewAlreadyExists(schema.GroupResource{Resource: "configmaps"}, "test-configmap")
|
|
// We need to return a non-nil unstructured object even though it's not used
|
|
dc.On("Create", mock.Anything).Return(&unstructured.Unstructured{}, alreadyExistsErr)
|
|
|
|
factory := &test.FakeDynamicFactory{}
|
|
factory.On("ClientForGroupVersionResource", mock.Anything, mock.Anything, mock.Anything).Return(dc, nil)
|
|
|
|
var buf bytes.Buffer
|
|
err := createOrApplyResource(r, factory, &buf, false)
|
|
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestCreateOrApplyResourceClientError(t *testing.T) {
|
|
r := &unstructured.Unstructured{
|
|
Object: map[string]any{
|
|
"apiVersion": "v1",
|
|
"kind": "ConfigMap",
|
|
"metadata": map[string]any{
|
|
"name": "test-configmap",
|
|
"namespace": "velero",
|
|
},
|
|
},
|
|
}
|
|
|
|
factory := &test.FakeDynamicFactory{}
|
|
expectedErr := errors.New("client creation error")
|
|
// Return error from ClientForGroupVersionResource
|
|
factory.On("ClientForGroupVersionResource", mock.Anything, mock.Anything, mock.Anything).Return(&test.FakeDynamicClient{}, expectedErr)
|
|
|
|
var buf bytes.Buffer
|
|
err := createOrApplyResource(r, factory, &buf, false)
|
|
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), expectedErr.Error())
|
|
}
|
|
|
|
func TestCreateOrApplyResourceApplyError(t *testing.T) {
|
|
r := &unstructured.Unstructured{
|
|
Object: map[string]any{
|
|
"apiVersion": "v1",
|
|
"kind": "ConfigMap",
|
|
"metadata": map[string]any{
|
|
"name": "test-configmap",
|
|
"namespace": "velero",
|
|
},
|
|
},
|
|
}
|
|
|
|
dc := &test.FakeDynamicClient{}
|
|
expectedErr := errors.New("apply error")
|
|
// Mock Apply to return an error
|
|
dc.On("Apply", mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{}, expectedErr)
|
|
|
|
factory := &test.FakeDynamicFactory{}
|
|
factory.On("ClientForGroupVersionResource", mock.Anything, mock.Anything, mock.Anything).Return(dc, nil)
|
|
|
|
var buf bytes.Buffer
|
|
err := createOrApplyResource(r, factory, &buf, true) // true for apply flag to use Apply
|
|
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), expectedErr.Error())
|
|
}
|
|
|
|
func TestInstallErrorAfterCreateClient(t *testing.T) {
|
|
// Create a test non-CRD resource
|
|
nonCRDResource := &unstructured.Unstructured{
|
|
Object: map[string]any{
|
|
"apiVersion": "v1",
|
|
"kind": "ConfigMap",
|
|
"metadata": map[string]any{
|
|
"name": "test-configmap",
|
|
},
|
|
},
|
|
}
|
|
|
|
resources := &unstructured.UnstructuredList{
|
|
Items: []unstructured.Unstructured{*nonCRDResource},
|
|
}
|
|
|
|
// Mock the factory to return a client that will succeed on ClientForGroupVersionResource
|
|
// but fail on Create
|
|
dc := &test.FakeDynamicClient{}
|
|
expectedErr := errors.New("create error after successful client creation")
|
|
dc.On("Create", mock.Anything).Return(&unstructured.Unstructured{}, expectedErr)
|
|
|
|
factory := &test.FakeDynamicFactory{}
|
|
factory.On("ClientForGroupVersionResource", mock.Anything, mock.Anything, mock.Anything).Return(dc, nil)
|
|
|
|
c := fake.NewClientBuilder().Build()
|
|
|
|
var buf bytes.Buffer
|
|
err := Install(factory, c, resources, &buf, false)
|
|
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), expectedErr.Error())
|
|
}
|
|
|
|
func TestInstallErrorOnCRDResource(t *testing.T) {
|
|
crdResource := &unstructured.Unstructured{
|
|
Object: map[string]any{
|
|
"apiVersion": "apiextensions.k8s.io/v1",
|
|
"kind": "CustomResourceDefinition",
|
|
"metadata": map[string]any{
|
|
"name": "test-crd",
|
|
},
|
|
},
|
|
}
|
|
|
|
resources := &unstructured.UnstructuredList{
|
|
Items: []unstructured.Unstructured{*crdResource},
|
|
}
|
|
|
|
dc := &test.FakeDynamicClient{}
|
|
expectedErr := errors.New("error creating CRD resource")
|
|
// We need to return a non-nil unstructured object even though it's not used
|
|
dc.On("Create", mock.Anything).Return(&unstructured.Unstructured{}, expectedErr)
|
|
|
|
factory := &test.FakeDynamicFactory{}
|
|
factory.On("ClientForGroupVersionResource", mock.Anything, mock.Anything, mock.Anything).Return(dc, nil)
|
|
|
|
c := fake.NewClientBuilder().Build()
|
|
|
|
var buf bytes.Buffer
|
|
err := Install(factory, c, resources, &buf, false)
|
|
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), expectedErr.Error())
|
|
}
|
|
|
|
func TestInstallWithApplyFlag(t *testing.T) {
|
|
// Create a test resource
|
|
testResource := &unstructured.Unstructured{
|
|
Object: map[string]any{
|
|
"apiVersion": "v1",
|
|
"kind": "ConfigMap",
|
|
"metadata": map[string]any{
|
|
"name": "test-configmap",
|
|
"namespace": "velero",
|
|
},
|
|
"data": map[string]any{
|
|
"key1": "value1",
|
|
},
|
|
},
|
|
}
|
|
|
|
resources := &unstructured.UnstructuredList{
|
|
Items: []unstructured.Unstructured{*testResource},
|
|
}
|
|
|
|
// Test case 1: Without apply flag (create)
|
|
{
|
|
dc := &test.FakeDynamicClient{}
|
|
// Expect Create to be called
|
|
dc.On("Create", mock.Anything).Return(testResource, nil)
|
|
// Apply should not be called
|
|
|
|
factory := &test.FakeDynamicFactory{}
|
|
factory.On("ClientForGroupVersionResource", mock.Anything, mock.Anything, mock.Anything).Return(dc, nil)
|
|
|
|
c := fake.NewClientBuilder().Build()
|
|
|
|
err := Install(factory, c, resources, os.Stdout, false)
|
|
require.NoError(t, err)
|
|
|
|
// Verify that Create was called and Apply was not
|
|
dc.AssertCalled(t, "Create", mock.Anything)
|
|
dc.AssertNotCalled(t, "Apply", mock.Anything, mock.Anything, mock.Anything)
|
|
}
|
|
|
|
// Test case 2: With apply flag
|
|
{
|
|
dc := &test.FakeDynamicClient{}
|
|
// Create should not be called
|
|
// Expect Apply to be called
|
|
dc.On("Apply", mock.Anything, mock.Anything, mock.Anything).Return(testResource, nil)
|
|
|
|
factory := &test.FakeDynamicFactory{}
|
|
factory.On("ClientForGroupVersionResource", mock.Anything, mock.Anything, mock.Anything).Return(dc, nil)
|
|
|
|
c := fake.NewClientBuilder().Build()
|
|
|
|
err := Install(factory, c, resources, os.Stdout, true)
|
|
require.NoError(t, err)
|
|
|
|
// Verify that Apply was called and Create was not
|
|
dc.AssertCalled(t, "Apply", mock.Anything, mock.Anything, mock.Anything)
|
|
dc.AssertNotCalled(t, "Create", mock.Anything)
|
|
}
|
|
}
|