Merge pull request #6497 from blackpiglet/6190_fix_part_2

Remove dependency of the legacy client code from pkg/cmd directory part 2
pull/6511/head
Xun Jiang/Bruce Jiang 2023-07-17 17:58:34 +08:00 committed by GitHub
commit 55987c3093
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 529 additions and 160 deletions

View File

@ -0,0 +1 @@
Remove dependency of the legacy client code from pkg/cmd directory part 2

View File

@ -141,6 +141,10 @@ func TestFactory(t *testing.T) {
kubebuilderClient, e := f.KubebuilderClient()
assert.Contains(t, e.Error(), fmt.Sprintf("Get \"%s/api?timeout=", test.expectedHost))
assert.Nil(t, kubebuilderClient)
kbClientWithWatch, e := f.KubebuilderWatchClient()
assert.Contains(t, e.Error(), fmt.Sprintf("Get \"%s/api?timeout=", test.expectedHost))
assert.Nil(t, kbClientWithWatch)
})
}
}

View File

@ -27,14 +27,12 @@ import (
flag "github.com/spf13/pflag"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"github.com/vmware-tanzu/velero/pkg/builder"
cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test"
"github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme"
veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec"
factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks"
cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec"
)
func TestNewDownloadCommand(t *testing.T) {
@ -42,7 +40,7 @@ func TestNewDownloadCommand(t *testing.T) {
f := &factorymocks.Factory{}
backupName := "backup-1"
kbclient := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build()
kbclient := velerotest.NewFakeControllerRuntimeClient(t)
err := kbclient.Create(context.Background(), builder.ForBackup(cmdtest.VeleroNameSpace, backupName).Result())
require.NoError(t, err)
err = kbclient.Create(context.Background(), builder.ForBackup(cmdtest.VeleroNameSpace, "bk-to-be-download").Result())

View File

@ -21,6 +21,8 @@ import (
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/client"
@ -38,19 +40,24 @@ func NewGetCommand(f client.Factory, use string) *cobra.Command {
err := output.ValidateFlags(c)
cmd.CheckError(err)
veleroClient, err := f.Client()
kbClient, err := f.KubebuilderClient()
cmd.CheckError(err)
var backups *api.BackupList
backups := new(api.BackupList)
if len(args) > 0 {
backups = new(api.BackupList)
for _, name := range args {
backup, err := veleroClient.VeleroV1().Backups(f.Namespace()).Get(context.TODO(), name, metav1.GetOptions{})
backup := new(api.Backup)
err := kbClient.Get(context.TODO(), kbclient.ObjectKey{Namespace: f.Namespace(), Name: name}, backup)
cmd.CheckError(err)
backups.Items = append(backups.Items, *backup)
}
} else {
backups, err = veleroClient.VeleroV1().Backups(f.Namespace()).List(context.TODO(), listOptions)
parsedSelector, err := labels.Parse(listOptions.LabelSelector)
cmd.CheckError(err)
err = kbClient.List(context.TODO(), backups, &kbclient.ListOptions{
LabelSelector: parsedSelector,
Namespace: f.Namespace(),
})
cmd.CheckError(err)
}

View File

@ -17,6 +17,7 @@ limitations under the License.
package backup
import (
"context"
"fmt"
"os"
"os/exec"
@ -24,15 +25,14 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test"
veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/builder"
factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks"
versionedmocks "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/mocks"
velerov1mocks "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1/mocks"
cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec"
)
func TestNewGetCommand(t *testing.T) {
@ -41,18 +41,16 @@ func TestNewGetCommand(t *testing.T) {
// create a factory
f := &factorymocks.Factory{}
backups := &velerov1mocks.BackupInterface{}
veleroV1 := &velerov1mocks.VeleroV1Interface{}
client := &versionedmocks.Interface{}
bk := &velerov1api.Backup{}
bkList := &velerov1api.BackupList{}
client := velerotest.NewFakeControllerRuntimeClient(t)
backups.On("List", mock.Anything, mock.Anything).Return(bkList, nil)
backups.On("Get", mock.Anything, mock.Anything, mock.Anything).Return(bk, nil)
veleroV1.On("Backups", mock.Anything).Return(backups, nil)
client.On("VeleroV1").Return(veleroV1, nil)
f.On("Client").Return(client, nil)
f.On("Namespace").Return(mock.Anything)
for _, backupName := range args {
backup := builder.ForBackup(cmdtest.VeleroNameSpace, backupName).ObjectMeta(builder.WithLabels("abc", "abc")).Result()
err := client.Create(context.Background(), backup, &kbclient.CreateOptions{})
require.NoError(t, err)
}
f.On("KubebuilderClient").Return(client, nil)
f.On("Namespace").Return(cmdtest.VeleroNameSpace)
// create command
c := NewGetCommand(f, "velero backup get")
@ -69,6 +67,28 @@ func TestNewGetCommand(t *testing.T) {
cmd := exec.Command(os.Args[0], []string{"-test.run=TestNewGetCommand"}...)
cmd.Env = append(os.Environ(), fmt.Sprintf("%s=1", cmdtest.CaptureFlag))
stdout, _, err := veleroexec.RunCommand(cmd)
require.NoError(t, err)
if err == nil {
output := strings.Split(stdout, "\n")
i := 0
for _, line := range output {
if strings.Contains(line, "New") {
i++
}
}
assert.Equal(t, len(args), i)
}
d := NewGetCommand(f, "velero backup get")
c.SetArgs([]string{"-l", "abc=abc"})
e = d.Execute()
assert.NoError(t, e)
cmd = exec.Command(os.Args[0], []string{"-test.run=TestNewGetCommand"}...)
cmd.Env = append(os.Environ(), fmt.Sprintf("%s=1", cmdtest.CaptureFlag))
stdout, _, err = veleroexec.RunCommand(cmd)
require.NoError(t, err)
if err == nil {
output := strings.Split(stdout, "\n")
@ -79,7 +99,5 @@ func TestNewGetCommand(t *testing.T) {
}
}
assert.Equal(t, len(args), i)
return
}
t.Fatalf("process ran with err %v, want backups by get()", err)
}

View File

@ -23,8 +23,9 @@ import (
"time"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/client"
@ -32,51 +33,84 @@ import (
"github.com/vmware-tanzu/velero/pkg/cmd/util/downloadrequest"
)
func NewLogsCommand(f client.Factory) *cobra.Command {
type LogsOptions struct {
Timeout time.Duration
InsecureSkipTLSVerify bool
CaCertFile string
Client kbclient.Client
BackupName string
}
func NewLogsOptions() LogsOptions {
config, err := client.LoadConfig()
if err != nil {
fmt.Fprintf(os.Stderr, "WARNING: Error reading config file: %v\n", err)
}
timeout := time.Minute
insecureSkipTLSVerify := false
caCertFile := config.CACertFile()
return LogsOptions{
Timeout: time.Minute,
InsecureSkipTLSVerify: false,
CaCertFile: config.CACertFile(),
}
}
func (l *LogsOptions) BindFlags(flags *pflag.FlagSet) {
flags.DurationVar(&l.Timeout, "timeout", l.Timeout, "How long to wait to receive logs.")
flags.BoolVar(&l.InsecureSkipTLSVerify, "insecure-skip-tls-verify", l.InsecureSkipTLSVerify, "If true, the object store's TLS certificate will not be checked for validity. This is insecure and susceptible to man-in-the-middle attacks. Not recommended for production.")
flags.StringVar(&l.CaCertFile, "cacert", l.CaCertFile, "Path to a certificate bundle to use when verifying TLS connections.")
}
func (l *LogsOptions) Run(c *cobra.Command, f client.Factory) error {
backup := new(velerov1api.Backup)
err := l.Client.Get(context.TODO(), kbclient.ObjectKey{Namespace: f.Namespace(), Name: l.BackupName}, backup)
if apierrors.IsNotFound(err) {
return fmt.Errorf("backup %q does not exist", l.BackupName)
} else if err != nil {
return fmt.Errorf("error checking for backup %q: %v", l.BackupName, err)
}
switch backup.Status.Phase {
case velerov1api.BackupPhaseCompleted, velerov1api.BackupPhasePartiallyFailed, velerov1api.BackupPhaseFailed, velerov1api.BackupPhaseWaitingForPluginOperations, velerov1api.BackupPhaseWaitingForPluginOperationsPartiallyFailed:
// terminal and waiting for plugin operations phases, do nothing.
default:
return fmt.Errorf("logs for backup %q are not available until it's finished processing, please wait "+
"until the backup has a phase of Completed or Failed and try again", l.BackupName)
}
err = downloadrequest.Stream(context.Background(), l.Client, f.Namespace(), l.BackupName, velerov1api.DownloadTargetKindBackupLog, os.Stdout, l.Timeout, l.InsecureSkipTLSVerify, l.CaCertFile)
return err
}
func (l *LogsOptions) Complete(args []string, f client.Factory) error {
if len(args) > 0 {
l.BackupName = args[0]
}
kbClient, err := f.KubebuilderClient()
if err != nil {
return err
}
l.Client = kbClient
return nil
}
func NewLogsCommand(f client.Factory) *cobra.Command {
l := NewLogsOptions()
c := &cobra.Command{
Use: "logs BACKUP",
Short: "Get backup logs",
Args: cobra.ExactArgs(1),
Run: func(c *cobra.Command, args []string) {
backupName := args[0]
veleroClient, err := f.Client()
err := l.Complete(args, f)
cmd.CheckError(err)
kbClient, err := f.KubebuilderClient()
cmd.CheckError(err)
backup, err := veleroClient.VeleroV1().Backups(f.Namespace()).Get(context.TODO(), backupName, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
cmd.Exit("Backup %q does not exist.", backupName)
} else if err != nil {
cmd.Exit("Error checking for backup %q: %v", backupName, err)
}
switch backup.Status.Phase {
case velerov1api.BackupPhaseCompleted, velerov1api.BackupPhasePartiallyFailed, velerov1api.BackupPhaseFailed, velerov1api.BackupPhaseWaitingForPluginOperations, velerov1api.BackupPhaseWaitingForPluginOperationsPartiallyFailed:
// terminal and waiting for plugin operations phases, do nothing.
default:
cmd.Exit("Logs for backup %q are not available until it's finished processing. Please wait "+
"until the backup has a phase of Completed or Failed and try again.", backupName)
}
err = downloadrequest.Stream(context.Background(), kbClient, f.Namespace(), backupName, velerov1api.DownloadTargetKindBackupLog, os.Stdout, timeout, insecureSkipTLSVerify, caCertFile)
err = l.Run(c, f)
cmd.CheckError(err)
},
}
c.Flags().DurationVar(&timeout, "timeout", timeout, "How long to wait to receive logs.")
c.Flags().BoolVar(&insecureSkipTLSVerify, "insecure-skip-tls-verify", insecureSkipTLSVerify, "If true, the object store's TLS certificate will not be checked for validity. This is insecure and susceptible to man-in-the-middle attacks. Not recommended for production.")
c.Flags().StringVar(&caCertFile, "cacert", caCertFile, "Path to a certificate bundle to use when verifying TLS connections.")
l.BindFlags(c.Flags())
return c
}

View File

@ -17,62 +17,130 @@ limitations under the License.
package backup
import (
"context"
"fmt"
"os"
"os/exec"
"strconv"
"testing"
"time"
flag "github.com/spf13/pflag"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"github.com/stretchr/testify/require"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/builder"
factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks"
cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test"
versionedmocks "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/mocks"
"github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme"
velerov1mocks "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1/mocks"
veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
)
func TestNewLogsCommand(t *testing.T) {
backupName := "bk-logs-1"
t.Run("Flag test", func(t *testing.T) {
l := NewLogsOptions()
flags := new(flag.FlagSet)
l.BindFlags(flags)
// create a factory
f := &factorymocks.Factory{}
timeout := "1m0s"
insecureSkipTLSVerify := "true"
caCertFile := "testing"
backups := &velerov1mocks.BackupInterface{}
veleroV1 := &velerov1mocks.VeleroV1Interface{}
client := &versionedmocks.Interface{}
bk := &velerov1api.Backup{}
bkList := &velerov1api.BackupList{}
kbclient := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build()
flags.Parse([]string{"--timeout", timeout})
flags.Parse([]string{"--insecure-skip-tls-verify", insecureSkipTLSVerify})
flags.Parse([]string{"--cacert", caCertFile})
backups.On("List", mock.Anything, mock.Anything).Return(bkList, nil)
backups.On("Get", mock.Anything, mock.Anything, mock.Anything).Return(bk, nil)
veleroV1.On("Backups", mock.Anything).Return(backups, nil)
client.On("VeleroV1").Return(veleroV1, nil)
f.On("Client").Return(client, nil)
f.On("Namespace").Return(mock.Anything)
f.On("KubebuilderClient").Return(kbclient, nil)
require.Equal(t, timeout, l.Timeout.String())
require.Equal(t, insecureSkipTLSVerify, strconv.FormatBool(l.InsecureSkipTLSVerify))
require.Equal(t, caCertFile, l.CaCertFile)
})
c := NewLogsCommand(f)
assert.Equal(t, "Get backup logs", c.Short)
t.Run("Backup not complete test", func(t *testing.T) {
backupName := "bk-logs-1"
if os.Getenv(cmdtest.CaptureFlag) == "1" {
c.SetArgs([]string{backupName})
e := c.Execute()
assert.NoError(t, e)
return
}
// create a factory
f := &factorymocks.Factory{}
cmd := exec.Command(os.Args[0], []string{"-test.run=TestNewLogsCommand"}...)
cmd.Env = append(os.Environ(), fmt.Sprintf("%s=1", cmdtest.CaptureFlag))
_, stderr, err := veleroexec.RunCommand(cmd)
kbClient := velerotest.NewFakeControllerRuntimeClient(t)
backup := builder.ForBackup(cmdtest.VeleroNameSpace, backupName).Result()
err := kbClient.Create(context.Background(), backup, &kbclient.CreateOptions{})
require.NoError(t, err)
if err != nil {
assert.Contains(t, stderr, fmt.Sprintf("Logs for backup \"%s\" are not available until it's finished processing", backupName))
return
}
t.Fatalf("process ran with err %v, want backup delete successfully", err)
f.On("Namespace").Return(cmdtest.VeleroNameSpace)
f.On("KubebuilderClient").Return(kbClient, nil)
c := NewLogsCommand(f)
assert.Equal(t, "Get backup logs", c.Short)
l := NewLogsOptions()
flags := new(flag.FlagSet)
l.BindFlags(flags)
err = l.Complete([]string{backupName}, f)
require.NoError(t, err)
err = l.Run(c, f)
require.Error(t, err)
require.Contains(t, err.Error(), fmt.Sprintf("logs for backup \"%s\" are not available until it's finished processing", backupName))
})
t.Run("Backup not exist test", func(t *testing.T) {
backupName := "not-exist"
// create a factory
f := &factorymocks.Factory{}
kbClient := velerotest.NewFakeControllerRuntimeClient(t)
f.On("Namespace").Return(cmdtest.VeleroNameSpace)
f.On("KubebuilderClient").Return(kbClient, nil)
c := NewLogsCommand(f)
assert.Equal(t, "Get backup logs", c.Short)
l := NewLogsOptions()
flags := new(flag.FlagSet)
l.BindFlags(flags)
err := l.Complete([]string{backupName}, f)
require.NoError(t, err)
err = l.Run(c, f)
require.Error(t, err)
require.Equal(t, fmt.Sprintf("backup \"%s\" does not exist", backupName), err.Error())
})
t.Run("Normal backup log test", func(t *testing.T) {
backupName := "bk-logs-1"
// create a factory
f := &factorymocks.Factory{}
kbClient := velerotest.NewFakeControllerRuntimeClient(t)
backup := builder.ForBackup(cmdtest.VeleroNameSpace, backupName).Phase(velerov1api.BackupPhaseCompleted).Result()
err := kbClient.Create(context.Background(), backup, &kbclient.CreateOptions{})
require.NoError(t, err)
f.On("Namespace").Return(cmdtest.VeleroNameSpace)
f.On("KubebuilderClient").Return(kbClient, nil)
c := NewLogsCommand(f)
assert.Equal(t, "Get backup logs", c.Short)
l := NewLogsOptions()
flags := new(flag.FlagSet)
l.BindFlags(flags)
err = l.Complete([]string{backupName}, f)
require.NoError(t, err)
timeout := time.After(3 * time.Second)
done := make(chan bool)
go func() {
err = l.Run(c, f)
require.Error(t, err)
}()
select {
case <-timeout:
t.Skip("Test didn't finish in time, because BSL is not in Available state.")
case <-done:
}
})
}

View File

@ -29,16 +29,9 @@ import (
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
veleroflag "github.com/vmware-tanzu/velero/pkg/cmd/util/flag"
"github.com/vmware-tanzu/velero/pkg/test"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks"
versionedmocks "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/mocks"
"github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme"
velerov1mocks "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1/mocks"
veleroflag "github.com/vmware-tanzu/velero/pkg/cmd/util/flag"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
)
func TestBuildBackupStorageLocationSetsNamespace(t *testing.T) {
@ -149,16 +142,7 @@ func TestCreateCommand_Run(t *testing.T) {
args := []string{name, "arg2"}
backups := &velerov1mocks.BackupInterface{}
veleroV1 := &velerov1mocks.VeleroV1Interface{}
client := &versionedmocks.Interface{}
bk := &velerov1api.Backup{}
kbclient := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build()
backups.On("Create", mock.Anything, mock.Anything, mock.Anything).Return(bk, nil)
veleroV1.On("Backups", mock.Anything).Return(backups, nil)
client.On("VeleroV1").Return(veleroV1, nil)
f.On("Client").Return(client, nil)
kbclient := velerotest.NewFakeControllerRuntimeClient(t)
f.On("Namespace").Return(mock.Anything)
f.On("KubebuilderClient").Return(kbclient, nil)
@ -179,7 +163,7 @@ func TestCreateCommand_Run(t *testing.T) {
assert.Equal(t, backupSyncPeriod, o.BackupSyncPeriod.String())
assert.Equal(t, validationFrequency, o.ValidationFrequency.String())
assert.Equal(t, true, reflect.DeepEqual(bslConfig, o.Config))
assert.Equal(t, true, test.CompareSlice(strings.Split(labels, ","), strings.Split(o.Labels.String(), ",")))
assert.Equal(t, true, velerotest.CompareSlice(strings.Split(labels, ","), strings.Split(o.Labels.String(), ",")))
assert.Equal(t, caCertFile, o.CACertFile)
assert.Equal(t, accessMode, o.AccessMode.String())

View File

@ -27,16 +27,10 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks"
cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test"
veleroflag "github.com/vmware-tanzu/velero/pkg/cmd/util/flag"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks"
versionedmocks "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/mocks"
"github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme"
velerov1mocks "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1/mocks"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec"
)
@ -44,16 +38,9 @@ func TestNewSetCommand(t *testing.T) {
backupName := "arg2"
// create a config for factory
f := &factorymocks.Factory{}
backups := &velerov1mocks.BackupInterface{}
veleroV1 := &velerov1mocks.VeleroV1Interface{}
client := &versionedmocks.Interface{}
bk := &velerov1api.Backup{}
kbclient := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build()
backups.On("Create", mock.Anything, mock.Anything, mock.Anything).Return(bk, nil)
veleroV1.On("Backups", mock.Anything).Return(backups, nil)
client.On("VeleroV1").Return(veleroV1, nil)
f.On("Client").Return(client, nil)
kbclient := velerotest.NewFakeControllerRuntimeClient(t)
f.On("Namespace").Return(mock.Anything)
f.On("KubebuilderClient").Return(kbclient, nil)
@ -97,16 +84,9 @@ func TestSetCommand_Execute(t *testing.T) {
if os.Getenv(cmdtest.CaptureFlag) == "1" {
// create a config for factory
f := &factorymocks.Factory{}
backups := &velerov1mocks.BackupInterface{}
veleroV1 := &velerov1mocks.VeleroV1Interface{}
client := &versionedmocks.Interface{}
bk := &velerov1api.Backup{}
kbclient := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build()
backups.On("Create", mock.Anything, mock.Anything, mock.Anything).Return(bk, nil)
veleroV1.On("Backups", mock.Anything).Return(backups, nil)
client.On("VeleroV1").Return(veleroV1, nil)
f.On("Client").Return(client, nil)
kbclient := velerotest.NewFakeControllerRuntimeClient(t)
f.On("Namespace").Return(mock.Anything)
f.On("KubebuilderClient").Return(kbclient, nil)

View File

@ -26,16 +26,17 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/pflag"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/client"
"github.com/vmware-tanzu/velero/pkg/cmd"
"github.com/vmware-tanzu/velero/pkg/cmd/util/flag"
"github.com/vmware-tanzu/velero/pkg/cmd/util/output"
veleroclient "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned"
v1 "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1"
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
"github.com/vmware-tanzu/velero/pkg/util/kube"
)
func NewCreateCommand(f client.Factory, use string) *cobra.Command {
@ -93,8 +94,7 @@ type CreateOptions struct {
Wait bool
AllowPartiallyFailed flag.OptionalBool
ItemOperationTimeout time.Duration
client veleroclient.Interface
client kbclient.WithWatch
}
func NewCreateOptions() *CreateOptions {
@ -153,7 +153,7 @@ func (o *CreateOptions) Complete(args []string, f client.Factory) error {
o.RestoreName = fmt.Sprintf("%s-%s", sourceName, time.Now().Format("20060102150405"))
}
client, err := f.Client()
client, err := f.KubebuilderWatchClient()
if err != nil {
return err
}
@ -186,15 +186,20 @@ func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Facto
switch {
case o.BackupName != "":
if _, err := o.client.VeleroV1().Backups(f.Namespace()).Get(context.TODO(), o.BackupName, metav1.GetOptions{}); err != nil {
backup := new(api.Backup)
if err := o.client.Get(context.TODO(), kbclient.ObjectKey{Namespace: f.Namespace(), Name: o.BackupName}, backup); err != nil {
return err
}
case o.ScheduleName != "":
backupItems, err := o.client.VeleroV1().Backups(f.Namespace()).List(context.TODO(), metav1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", api.ScheduleNameLabel, o.ScheduleName)})
backupList := new(api.BackupList)
err := o.client.List(context.TODO(), backupList, &kbclient.ListOptions{
LabelSelector: labels.SelectorFromSet(map[string]string{api.ScheduleNameLabel: o.ScheduleName}),
Namespace: f.Namespace(),
})
if err != nil {
return err
}
if len(backupItems.Items) == 0 {
if len(backupList.Items) == 0 {
return errors.Errorf("No backups found for the schedule %s", o.ScheduleName)
}
}
@ -248,14 +253,18 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error {
// PartiallyFailed backup for the provided schedule, and use that specific backup
// to restore from.
if o.ScheduleName != "" && boolptr.IsSetToTrue(o.AllowPartiallyFailed.Value) {
backups, err := o.client.VeleroV1().Backups(f.Namespace()).List(context.TODO(), metav1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", api.ScheduleNameLabel, o.ScheduleName)})
backupList := new(api.BackupList)
err := o.client.List(context.TODO(), backupList, &kbclient.ListOptions{
LabelSelector: labels.SelectorFromSet(map[string]string{api.ScheduleNameLabel: o.ScheduleName}),
Namespace: f.Namespace(),
})
if err != nil {
return err
}
// if we find a Completed or PartiallyFailed backup for the schedule, restore specifically from that backup. If we don't
// find one, proceed as-is -- the Velero server will handle validation.
if backup := mostRecentBackup(backups.Items, api.BackupPhaseCompleted, api.BackupPhasePartiallyFailed); backup != nil {
if backup := mostRecentBackup(backupList.Items, api.BackupPhaseCompleted, api.BackupPhasePartiallyFailed); backup != nil {
// TODO(sk): this is kind of a hack -- we should revisit this and probably
// move this logic to the server side or otherwise solve this problem.
o.BackupName = backup.Name
@ -299,7 +308,6 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error {
return err
}
var restoreInformer cache.SharedIndexInformer
var updates chan *api.Restore
if o.Wait {
stop := make(chan struct{})
@ -307,7 +315,12 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error {
updates = make(chan *api.Restore)
restoreInformer = v1.NewRestoreInformer(o.client, f.Namespace(), 0, nil)
lw := kube.InternalLW{
Client: o.client,
Namespace: f.Namespace(),
ObjectList: new(api.RestoreList),
}
restoreInformer := cache.NewSharedInformer(&lw, &api.Restore{}, time.Second)
restoreInformer.AddEventHandler(
cache.FilteringResourceEventHandler{
@ -339,7 +352,7 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error {
go restoreInformer.Run(stop)
}
restore, err := o.client.VeleroV1().Restores(restore.Namespace).Create(context.TODO(), restore, metav1.CreateOptions{})
err := o.client.Create(context.TODO(), restore, &kbclient.CreateOptions{})
if err != nil {
return err
}

View File

@ -0,0 +1,174 @@
/*
Copyright 2020 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 restore
import (
"context"
"fmt"
"testing"
"time"
"github.com/spf13/pflag"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
controllerclient "sigs.k8s.io/controller-runtime/pkg/client"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/builder"
factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks"
cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
)
func TestIsResourcePolicyValid(t *testing.T) {
require.True(t, isResourcePolicyValid(string(velerov1api.PolicyTypeNone)))
require.True(t, isResourcePolicyValid(string(velerov1api.PolicyTypeUpdate)))
require.False(t, isResourcePolicyValid(""))
}
func TestMostRecentBackup(t *testing.T) {
backups := []velerov1api.Backup{
*builder.ForBackup(cmdtest.VeleroNameSpace, "backup0").StartTimestamp(time.Now().Add(3 * time.Second)).Phase(velerov1api.BackupPhaseDeleting).Result(),
*builder.ForBackup(cmdtest.VeleroNameSpace, "backup1").StartTimestamp(time.Now().Add(time.Second)).Phase(velerov1api.BackupPhaseCompleted).Result(),
*builder.ForBackup(cmdtest.VeleroNameSpace, "backup2").StartTimestamp(time.Now().Add(2 * time.Second)).Phase(velerov1api.BackupPhasePartiallyFailed).Result(),
}
expectedBackup := builder.ForBackup(cmdtest.VeleroNameSpace, "backup2").StartTimestamp(time.Now().Add(2 * time.Second)).Phase(velerov1api.BackupPhasePartiallyFailed).Result()
resultBackup := mostRecentBackup(backups, velerov1api.BackupPhaseCompleted, velerov1api.BackupPhasePartiallyFailed)
require.Equal(t, expectedBackup.Name, resultBackup.Name)
}
func TestCreateCommand(t *testing.T) {
name := "nameToBeCreated"
args := []string{name}
t.Run("create a backup create command with full options except fromSchedule and wait, then run by create option", func(t *testing.T) {
// create a factory
f := &factorymocks.Factory{}
// create command
cmd := NewCreateCommand(f, "")
require.Equal(t, "Create a restore", cmd.Short)
backupName := "backup1"
scheduleName := "schedule1"
restoreVolumes := "true"
preserveNodePorts := "true"
labels := "c=foo,b=woo"
includeNamespaces := "app1,app2"
excludeNamespaces := "pod1,pod2,pod3"
existingResourcePolicy := "none"
includeResources := "sc,sts"
excludeResources := "job"
statusIncludeResources := "sc,sts"
statusExcludeResources := "job"
namespaceMappings := "a:b"
selector := "foo=bar"
includeClusterResources := "true"
allowPartiallyFailed := "true"
itemOperationTimeout := "10m0s"
flags := new(pflag.FlagSet)
o := NewCreateOptions()
o.BindFlags(flags)
flags.Parse([]string{"--from-backup", backupName})
flags.Parse([]string{"--from-schedule", scheduleName})
flags.Parse([]string{"--restore-volumes", restoreVolumes})
flags.Parse([]string{"--preserve-nodeports", preserveNodePorts})
flags.Parse([]string{"--labels", labels})
flags.Parse([]string{"--existing-resource-policy", existingResourcePolicy})
flags.Parse([]string{"--include-namespaces", includeNamespaces})
flags.Parse([]string{"--exclude-namespaces", excludeNamespaces})
flags.Parse([]string{"--include-resources", includeResources})
flags.Parse([]string{"--exclude-resources", excludeResources})
flags.Parse([]string{"--status-include-resources", statusIncludeResources})
flags.Parse([]string{"--status-exclude-resources", statusExcludeResources})
flags.Parse([]string{"--namespace-mappings", namespaceMappings})
flags.Parse([]string{"--selector", selector})
flags.Parse([]string{"--include-cluster-resources", includeClusterResources})
flags.Parse([]string{"--allow-partially-failed", allowPartiallyFailed})
flags.Parse([]string{"--item-operation-timeout", itemOperationTimeout})
client := velerotest.NewFakeControllerRuntimeClient(t).(kbclient.WithWatch)
f.On("Namespace").Return(mock.Anything)
f.On("KubebuilderWatchClient").Return(client, nil)
//Complete
e := o.Complete(args, f)
require.NoError(t, e)
//Validate
e = o.Validate(cmd, args, f)
require.Contains(t, e.Error(), "either a backup or schedule must be specified, but not both")
//cmd
e = o.Run(cmd, f)
require.NoError(t, e)
require.Equal(t, backupName, o.BackupName)
require.Equal(t, scheduleName, o.ScheduleName)
require.Equal(t, restoreVolumes, o.RestoreVolumes.String())
require.Equal(t, preserveNodePorts, o.PreserveNodePorts.String())
require.Equal(t, labels, o.Labels.String())
require.Equal(t, includeNamespaces, o.IncludeNamespaces.String())
require.Equal(t, excludeNamespaces, o.ExcludeNamespaces.String())
require.Equal(t, existingResourcePolicy, o.ExistingResourcePolicy)
require.Equal(t, includeResources, o.IncludeResources.String())
require.Equal(t, excludeResources, o.ExcludeResources.String())
require.Equal(t, statusIncludeResources, o.StatusIncludeResources.String())
require.Equal(t, statusExcludeResources, o.StatusExcludeResources.String())
require.Equal(t, namespaceMappings, o.NamespaceMappings.String())
require.Equal(t, selector, o.Selector.String())
require.Equal(t, includeClusterResources, o.IncludeClusterResources.String())
require.Equal(t, allowPartiallyFailed, o.AllowPartiallyFailed.String())
require.Equal(t, itemOperationTimeout, o.ItemOperationTimeout.String())
})
t.Run("create a restore create from schedule", func(t *testing.T) {
f := &factorymocks.Factory{}
c := NewCreateCommand(f, "")
require.Equal(t, "Create a restore", c.Short)
flags := new(pflag.FlagSet)
o := NewCreateOptions()
o.BindFlags(flags)
fromSchedule := "schedule-name-1"
flags.Parse([]string{"--from-schedule", fromSchedule})
fmt.Printf("debug, restore options: %+v\n", o)
kbclient := velerotest.NewFakeControllerRuntimeClient(t).(kbclient.WithWatch)
schedule := builder.ForSchedule(cmdtest.VeleroNameSpace, fromSchedule).Result()
kbclient.Create(context.Background(), schedule, &controllerclient.CreateOptions{})
f.On("Namespace").Return(cmdtest.VeleroNameSpace)
f.On("KubebuilderWatchClient").Return(kbclient, nil)
require.NoError(t, o.Complete(args, f))
require.NoError(t, o.Run(c, f))
})
}

View File

@ -0,0 +1,34 @@
/*
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 restore
import (
"testing"
"github.com/stretchr/testify/assert"
factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks"
)
func TestNewRestoreCommand(t *testing.T) {
// create a factory
f := &factorymocks.Factory{}
// create command
cmd := NewCommand(f)
assert.Equal(t, "Work with restores", cmd.Short)
}

View File

@ -0,0 +1,54 @@
/*
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"
"time"
"github.com/stretchr/testify/require"
"k8s.io/client-go/tools/cache"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
)
func TestInternalLW(t *testing.T) {
stop := make(chan struct{})
client := velerotest.NewFakeControllerRuntimeClient(t).(kbclient.WithWatch)
lw := InternalLW{
Client: client,
Namespace: cmdtest.VeleroNameSpace,
ObjectList: new(velerov1api.BackupList),
}
restoreInformer := cache.NewSharedInformer(&lw, &velerov1api.BackupList{}, time.Second)
go restoreInformer.Run(stop)
time.Sleep(1 * time.Second)
close(stop)
backupList := new(velerov1api.BackupList)
err := client.List(context.Background(), backupList)
require.NoError(t, err)
_, err = client.Watch(context.Background(), backupList)
require.NoError(t, err)
}