mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-08-19 13:46:08 +00:00
Support pause/unpause schedules
Support pause/unpause schedule Fixes #2363 Signed-off-by: Wenkai Yin(尹文开) <yinw@vmware.com>
This commit is contained in:
@@ -27,24 +27,20 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/client"
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd/util/flag"
|
||||
clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned"
|
||||
)
|
||||
|
||||
// DeleteOptions contains parameters used for deleting a restore.
|
||||
type DeleteOptions struct {
|
||||
Names []string
|
||||
all bool
|
||||
Selector flag.LabelSelector
|
||||
Confirm bool
|
||||
Client clientset.Interface
|
||||
Namespace string
|
||||
singularTypeName string
|
||||
*SelectOptions
|
||||
Confirm bool
|
||||
Client clientset.Interface
|
||||
Namespace string
|
||||
}
|
||||
|
||||
func NewDeleteOptions(singularTypeName string) *DeleteOptions {
|
||||
o := &DeleteOptions{}
|
||||
o.singularTypeName = singularTypeName
|
||||
o.SelectOptions = NewSelectOptions("delete", singularTypeName)
|
||||
return o
|
||||
}
|
||||
|
||||
@@ -56,8 +52,7 @@ func (o *DeleteOptions) Complete(f client.Factory, args []string) error {
|
||||
return err
|
||||
}
|
||||
o.Client = client
|
||||
o.Names = args
|
||||
return nil
|
||||
return o.SelectOptions.Complete(args)
|
||||
}
|
||||
|
||||
// Validate validates the fields of the DeleteOptions struct.
|
||||
@@ -65,23 +60,14 @@ func (o *DeleteOptions) Validate(c *cobra.Command, f client.Factory, args []stri
|
||||
if o.Client == nil {
|
||||
return errors.New("Velero client is not set; unable to proceed")
|
||||
}
|
||||
var (
|
||||
hasNames = len(o.Names) > 0
|
||||
hasAll = o.all
|
||||
hasSelector = o.Selector.LabelSelector != nil
|
||||
)
|
||||
if !xor(hasNames, hasAll, hasSelector) {
|
||||
return errors.New("you must specify exactly one of: specific " + o.singularTypeName + " name(s), the --all flag, or the --selector flag")
|
||||
}
|
||||
|
||||
return nil
|
||||
return o.SelectOptions.Validate()
|
||||
}
|
||||
|
||||
// BindFlags binds options for this command to flags.
|
||||
func (o *DeleteOptions) BindFlags(flags *pflag.FlagSet) {
|
||||
flags.BoolVar(&o.Confirm, "confirm", o.Confirm, "Confirm deletion")
|
||||
flags.BoolVar(&o.all, "all", o.all, "Delete all "+o.singularTypeName+"s")
|
||||
flags.VarP(&o.Selector, "selector", "l", "Delete all "+o.singularTypeName+"s matching this label selector.")
|
||||
o.SelectOptions.BindFlags(flags)
|
||||
}
|
||||
|
||||
// GetConfirmation ensures that the user confirms the action before proceeding.
|
||||
|
||||
@@ -82,6 +82,7 @@ type CreateOptions struct {
|
||||
BackupOptions *backup.CreateOptions
|
||||
Schedule string
|
||||
UseOwnerReferencesInBackup bool
|
||||
Paused bool
|
||||
|
||||
labelSelector *metav1.LabelSelector
|
||||
}
|
||||
@@ -96,6 +97,7 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) {
|
||||
o.BackupOptions.BindFlags(flags)
|
||||
flags.StringVar(&o.Schedule, "schedule", o.Schedule, "A cron expression specifying a recurring schedule for this backup to run")
|
||||
flags.BoolVar(&o.UseOwnerReferencesInBackup, "use-owner-references-in-backup", o.UseOwnerReferencesInBackup, "Specifies whether to use OwnerReferences on backups created by this Schedule. Notice: if set to true, when schedule is deleted, backups will be deleted too.")
|
||||
flags.BoolVar(&o.Paused, "paused", o.Paused, "Specifies whether the newly created schedule is paused or not.")
|
||||
}
|
||||
|
||||
func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Factory) error {
|
||||
@@ -149,6 +151,7 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error {
|
||||
},
|
||||
Schedule: o.Schedule,
|
||||
UseOwnerReferencesInBackup: &o.UseOwnerReferencesInBackup,
|
||||
Paused: o.Paused,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
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 schedule
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
kubeerrs "k8s.io/apimachinery/pkg/util/errors"
|
||||
|
||||
velerov1api "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/cli"
|
||||
)
|
||||
|
||||
// NewPauseCommand creates the command for pause
|
||||
func NewPauseCommand(f client.Factory, use string) *cobra.Command {
|
||||
o := cli.NewSelectOptions("pause", "schedule")
|
||||
|
||||
c := &cobra.Command{
|
||||
Use: use,
|
||||
Short: "Pause schedules",
|
||||
Example: ` # Pause a schedule named "schedule-1".
|
||||
velero schedule pause schedule-1
|
||||
|
||||
# Pause schedules named "schedule-1" and "schedule-2".
|
||||
velero schedule pause schedule-1 schedule-2
|
||||
|
||||
# Pause all schedules labelled with "foo=bar".
|
||||
velero schedule pause --selector foo=bar
|
||||
|
||||
# Pause all schedules.
|
||||
velero schedule pause --all`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
cmd.CheckError(o.Complete(args))
|
||||
cmd.CheckError(o.Validate())
|
||||
cmd.CheckError(runPause(f, o, true))
|
||||
},
|
||||
}
|
||||
|
||||
o.BindFlags(c.Flags())
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func runPause(f client.Factory, o *cli.SelectOptions, paused bool) error {
|
||||
client, err := f.Client()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
schedules []*velerov1api.Schedule
|
||||
errs []error
|
||||
)
|
||||
switch {
|
||||
case len(o.Names) > 0:
|
||||
for _, name := range o.Names {
|
||||
schedule, err := client.VeleroV1().Schedules(f.Namespace()).Get(context.TODO(), name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
errs = append(errs, errors.WithStack(err))
|
||||
continue
|
||||
}
|
||||
schedules = append(schedules, schedule)
|
||||
}
|
||||
default:
|
||||
selector := labels.Everything().String()
|
||||
if o.Selector.LabelSelector != nil {
|
||||
selector = o.Selector.String()
|
||||
}
|
||||
res, err := client.VeleroV1().Schedules(f.Namespace()).List(context.TODO(), metav1.ListOptions{
|
||||
LabelSelector: selector,
|
||||
})
|
||||
if err != nil {
|
||||
errs = append(errs, errors.WithStack(err))
|
||||
}
|
||||
|
||||
for i := range res.Items {
|
||||
schedules = append(schedules, &res.Items[i])
|
||||
}
|
||||
}
|
||||
if len(schedules) == 0 {
|
||||
fmt.Println("No schedules found")
|
||||
return nil
|
||||
}
|
||||
|
||||
msg := "paused"
|
||||
if !paused {
|
||||
msg = "unpaused"
|
||||
}
|
||||
for _, schedule := range schedules {
|
||||
if schedule.Spec.Paused == paused {
|
||||
fmt.Printf("Schedule %s is already %s, skip\n", schedule.Name, msg)
|
||||
continue
|
||||
}
|
||||
schedule.Spec.Paused = paused
|
||||
if _, err := client.VeleroV1().Schedules(schedule.Namespace).Update(context.TODO(), schedule, metav1.UpdateOptions{}); err != nil {
|
||||
return errors.Wrapf(err, "failed to update schedule %s", schedule.Name)
|
||||
}
|
||||
fmt.Printf("Schedule %s %s successfully\n", schedule.Name, msg)
|
||||
}
|
||||
return kubeerrs.NewAggregate(errs)
|
||||
}
|
||||
@@ -34,6 +34,8 @@ func NewCommand(f client.Factory) *cobra.Command {
|
||||
NewGetCommand(f, "get"),
|
||||
NewDescribeCommand(f, "describe"),
|
||||
NewDeleteCommand(f, "delete"),
|
||||
NewPauseCommand(f, "pause"),
|
||||
NewUnpauseCommand(f, "unpause"),
|
||||
)
|
||||
|
||||
return c
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
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 schedule
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/client"
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd"
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd/cli"
|
||||
)
|
||||
|
||||
// NewUnpauseCommand creates the command for unpause
|
||||
func NewUnpauseCommand(f client.Factory, use string) *cobra.Command {
|
||||
o := cli.NewSelectOptions("pause", "schedule")
|
||||
|
||||
c := &cobra.Command{
|
||||
Use: use,
|
||||
Short: "Unpause schedules",
|
||||
Example: ` # Unpause a schedule named "schedule-1".
|
||||
velero schedule unpause schedule-1
|
||||
|
||||
# Unpause schedules named "schedule-1" and "schedule-2".
|
||||
velero schedule unpause schedule-1 schedule-2
|
||||
|
||||
# Unpause all schedules labelled with "foo=bar".
|
||||
velero schedule unpause --selector foo=bar
|
||||
|
||||
# Unpause all schedules.
|
||||
velero schedule unpause --all`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
cmd.CheckError(o.Complete(args))
|
||||
cmd.CheckError(o.Validate())
|
||||
cmd.CheckError(runPause(f, o, false))
|
||||
},
|
||||
}
|
||||
|
||||
o.BindFlags(c.Flags())
|
||||
|
||||
return c
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
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 cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd/util/flag"
|
||||
)
|
||||
|
||||
// SelectOptions defines the options for selecting resources
|
||||
type SelectOptions struct {
|
||||
Names []string
|
||||
All bool
|
||||
Selector flag.LabelSelector
|
||||
CMD string
|
||||
SingularTypeName string
|
||||
}
|
||||
|
||||
// NewSelectOptions creates a new option for selector
|
||||
func NewSelectOptions(cmd, singularTypeName string) *SelectOptions {
|
||||
return &SelectOptions{
|
||||
CMD: cmd,
|
||||
SingularTypeName: singularTypeName,
|
||||
}
|
||||
}
|
||||
|
||||
// Complete fills in the correct values for all the options.
|
||||
func (o *SelectOptions) Complete(args []string) error {
|
||||
o.Names = args
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the fields of the SelectOptions struct.
|
||||
func (o *SelectOptions) Validate() error {
|
||||
var (
|
||||
hasNames = len(o.Names) > 0
|
||||
hasAll = o.All
|
||||
hasSelector = o.Selector.LabelSelector != nil
|
||||
)
|
||||
if !xor(hasNames, hasAll, hasSelector) {
|
||||
return errors.New("you must specify exactly one of: specific " + o.SingularTypeName + " name(s), the --all flag, or the --selector flag")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BindFlags binds options for this command to flags.
|
||||
func (o *SelectOptions) BindFlags(flags *pflag.FlagSet) {
|
||||
flags.BoolVar(&o.All, "all", o.All, strings.Title(o.CMD)+" all "+o.SingularTypeName+"s")
|
||||
flags.VarP(&o.Selector, "selector", "l", strings.Title(o.CMD)+" all "+o.SingularTypeName+"s matching this label selector.")
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
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 cli
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd/util/flag"
|
||||
)
|
||||
|
||||
func TestCompleteOfSelectOption(t *testing.T) {
|
||||
option := &SelectOptions{}
|
||||
args := []string{"arg1", "arg2"}
|
||||
require.Nil(t, option.Complete(args))
|
||||
assert.Equal(t, args, option.Names)
|
||||
}
|
||||
|
||||
func TestValidateOfSelectOption(t *testing.T) {
|
||||
option := &SelectOptions{
|
||||
Names: nil,
|
||||
Selector: flag.LabelSelector{},
|
||||
All: false,
|
||||
}
|
||||
assert.NotNil(t, option.Validate())
|
||||
|
||||
option.All = true
|
||||
assert.Nil(t, option.Validate())
|
||||
}
|
||||
@@ -52,6 +52,9 @@ func DescribeSchedule(schedule *v1.Schedule) string {
|
||||
}
|
||||
}
|
||||
|
||||
d.Println()
|
||||
d.Printf("Paused:\t%t\n", schedule.Spec.Paused)
|
||||
|
||||
d.Println()
|
||||
DescribeScheduleSpec(d, schedule.Spec)
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ var (
|
||||
{Name: "Backup TTL"},
|
||||
{Name: "Last Backup"},
|
||||
{Name: "Selector"},
|
||||
{Name: "Paused"},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -71,6 +72,7 @@ func printSchedule(schedule *v1.Schedule) []metav1.TableRow {
|
||||
schedule.Spec.Template.TTL.Duration,
|
||||
humanReadableTimeFromNow(lastBackupTime),
|
||||
metav1.FormatLabelSelector(schedule.Spec.Template.LabelSelector),
|
||||
schedule.Spec.Paused,
|
||||
)
|
||||
|
||||
return []metav1.TableRow{row}
|
||||
|
||||
Reference in New Issue
Block a user