196 lines
5.2 KiB
Go
196 lines
5.2 KiB
Go
package repomantenance
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/bombsimon/logrusr/v3"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/pflag"
|
|
corev1api "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/client-go/kubernetes"
|
|
ctrl "sigs.k8s.io/controller-runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
"github.com/vmware-tanzu/velero/internal/credentials"
|
|
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
|
velerocli "github.com/vmware-tanzu/velero/pkg/client"
|
|
"github.com/vmware-tanzu/velero/pkg/repository"
|
|
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
|
|
"github.com/vmware-tanzu/velero/pkg/util/logging"
|
|
|
|
repokey "github.com/vmware-tanzu/velero/pkg/repository/keys"
|
|
"github.com/vmware-tanzu/velero/pkg/repository/maintenance"
|
|
repomanager "github.com/vmware-tanzu/velero/pkg/repository/manager"
|
|
)
|
|
|
|
type Options struct {
|
|
RepoName string
|
|
BackupStorageLocation string
|
|
RepoType string
|
|
ResourceTimeout time.Duration
|
|
LogLevelFlag *logging.LevelFlag
|
|
FormatFlag *logging.FormatFlag
|
|
}
|
|
|
|
func (o *Options) BindFlags(flags *pflag.FlagSet) {
|
|
flags.StringVar(&o.RepoName, "repo-name", "", "namespace of the pod/volume that the snapshot is for")
|
|
flags.StringVar(&o.BackupStorageLocation, "backup-storage-location", "", "backup's storage location name")
|
|
flags.StringVar(&o.RepoType, "repo-type", velerov1api.BackupRepositoryTypeKopia, "type of the repository where the snapshot is stored")
|
|
flags.Var(o.LogLevelFlag, "log-level", fmt.Sprintf("The level at which to log. Valid values are %s.", strings.Join(o.LogLevelFlag.AllowedValues(), ", ")))
|
|
flags.Var(o.FormatFlag, "log-format", fmt.Sprintf("The format for log output. Valid values are %s.", strings.Join(o.FormatFlag.AllowedValues(), ", ")))
|
|
}
|
|
|
|
func NewCommand(f velerocli.Factory) *cobra.Command {
|
|
o := &Options{
|
|
LogLevelFlag: logging.LogLevelFlag(logrus.InfoLevel),
|
|
FormatFlag: logging.NewFormatFlag(),
|
|
}
|
|
cmd := &cobra.Command{
|
|
Use: "repo-maintenance",
|
|
Hidden: true,
|
|
Short: "VELERO INTERNAL COMMAND ONLY - not intended to be run directly by users",
|
|
Run: func(c *cobra.Command, args []string) {
|
|
o.Run(f)
|
|
},
|
|
}
|
|
|
|
o.BindFlags(cmd.Flags())
|
|
return cmd
|
|
}
|
|
|
|
func (o *Options) Run(f velerocli.Factory) {
|
|
logger := logging.DefaultLogger(o.LogLevelFlag.Parse(), o.FormatFlag.Parse())
|
|
logger.SetOutput(os.Stdout)
|
|
|
|
ctrl.SetLogger(logrusr.New(logger))
|
|
|
|
pruneError := o.runRepoPrune(f, f.Namespace(), logger)
|
|
defer func() {
|
|
if pruneError != nil {
|
|
os.Exit(1)
|
|
}
|
|
}()
|
|
|
|
if pruneError != nil {
|
|
os.Stdout.WriteString(fmt.Sprintf("%s%v", maintenance.TerminationLogIndicator, pruneError))
|
|
}
|
|
}
|
|
|
|
func (o *Options) initClient(f velerocli.Factory) (client.Client, error) {
|
|
scheme := runtime.NewScheme()
|
|
err := velerov1api.AddToScheme(scheme)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to add velero scheme")
|
|
}
|
|
|
|
err = corev1api.AddToScheme(scheme)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to add api core scheme")
|
|
}
|
|
|
|
config, err := f.ClientConfig()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to get client config")
|
|
}
|
|
|
|
cli, err := client.New(config, client.Options{
|
|
Scheme: scheme,
|
|
})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to create client")
|
|
}
|
|
|
|
return cli, nil
|
|
}
|
|
|
|
func initRepoManager(namespace string, cli client.Client, kubeClient kubernetes.Interface, logger logrus.FieldLogger) (repomanager.Manager, error) {
|
|
// ensure the repo key secret is set up
|
|
if err := repokey.EnsureCommonRepositoryKey(kubeClient.CoreV1(), namespace); err != nil {
|
|
return nil, errors.Wrap(err, "failed to ensure repository key")
|
|
}
|
|
|
|
repoLocker := repository.NewRepoLocker()
|
|
|
|
credentialFileStore, err := credentials.NewNamespacedFileStore(
|
|
cli,
|
|
namespace,
|
|
credentials.DefaultStoreDirectory(),
|
|
filesystem.NewFileSystem(),
|
|
)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to create namespaced file store")
|
|
}
|
|
|
|
credentialSecretStore, err := credentials.NewNamespacedSecretStore(cli, namespace)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to create namespaced secret store")
|
|
}
|
|
|
|
return repomanager.NewManager(
|
|
namespace,
|
|
cli,
|
|
repoLocker,
|
|
credentialFileStore,
|
|
credentialSecretStore,
|
|
logger,
|
|
), nil
|
|
}
|
|
|
|
func (o *Options) runRepoPrune(f velerocli.Factory, namespace string, logger logrus.FieldLogger) error {
|
|
cli, err := o.initClient(f)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
kubeClient, err := f.KubeClient()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var repo *velerov1api.BackupRepository
|
|
retry := 10
|
|
for {
|
|
repo, err = repository.GetBackupRepository(context.Background(), cli, namespace,
|
|
repository.BackupRepositoryKey{
|
|
VolumeNamespace: o.RepoName,
|
|
BackupLocation: o.BackupStorageLocation,
|
|
RepositoryType: o.RepoType,
|
|
}, true)
|
|
if err == nil {
|
|
break
|
|
}
|
|
|
|
retry--
|
|
if retry == 0 {
|
|
break
|
|
}
|
|
|
|
logger.WithError(err).Warn("Failed to retrieve backup repo, need retry")
|
|
|
|
time.Sleep(time.Second)
|
|
}
|
|
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to get backup repository")
|
|
}
|
|
|
|
manager, err := initRepoManager(namespace, cli, kubeClient, logger)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = manager.PruneRepo(repo)
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to prune repo")
|
|
}
|
|
|
|
return nil
|
|
}
|