mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-17 05:24:26 +00:00
add restic integration for doing pod volume backups/restores
Signed-off-by: Steve Kriss <steve@heptio.com>
This commit is contained in:
+32
-3
@@ -21,10 +21,11 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
)
|
||||
|
||||
// NamespaceAndName returns a string in the format <namespace>/<name>
|
||||
@@ -39,7 +40,7 @@ func NamespaceAndName(objMeta metav1.Object) string {
|
||||
// a bool indicating whether or not the namespace was created, and an error if the create failed
|
||||
// for a reason other than that the namespace already exists. Note that in the case where the
|
||||
// namespace already exists, this function will return (false, nil).
|
||||
func EnsureNamespaceExists(namespace *v1.Namespace, client corev1.NamespaceInterface) (bool, error) {
|
||||
func EnsureNamespaceExists(namespace *corev1api.Namespace, client corev1client.NamespaceInterface) (bool, error) {
|
||||
if _, err := client.Create(namespace); err == nil {
|
||||
return true, nil
|
||||
} else if apierrors.IsAlreadyExists(err) {
|
||||
@@ -48,3 +49,31 @@ func EnsureNamespaceExists(namespace *v1.Namespace, client corev1.NamespaceInter
|
||||
return false, errors.Wrapf(err, "error creating namespace %s", namespace.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// GetVolumeDirectory gets the name of the directory on the host, under /var/lib/kubelet/pods/<podUID>/volumes/,
|
||||
// where the specified volume lives.
|
||||
func GetVolumeDirectory(pod *corev1api.Pod, volumeName string, pvcLister corev1listers.PersistentVolumeClaimLister) (string, error) {
|
||||
var volume *corev1api.Volume
|
||||
|
||||
for _, item := range pod.Spec.Volumes {
|
||||
if item.Name == volumeName {
|
||||
volume = &item
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if volume == nil {
|
||||
return "", errors.New("volume not found in pod")
|
||||
}
|
||||
|
||||
if volume.VolumeSource.PersistentVolumeClaim == nil {
|
||||
return volume.Name, nil
|
||||
}
|
||||
|
||||
pvc, err := pvcLister.PersistentVolumeClaims(pod.Namespace).Get(volume.VolumeSource.PersistentVolumeClaim.ClaimName)
|
||||
if err != nil {
|
||||
return "", errors.WithStack(err)
|
||||
}
|
||||
|
||||
return pvc.Spec.VolumeName, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// DefaultHooks returns a slice of the default
|
||||
// logrus hooks to be used by a logger.
|
||||
func DefaultHooks() []logrus.Hook {
|
||||
return []logrus.Hook{
|
||||
&LogLocationHook{},
|
||||
&ErrorLocationHook{},
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultLogger returns a Logger with the default properties
|
||||
// and hooks.
|
||||
func DefaultLogger(level logrus.Level) *logrus.Logger {
|
||||
logger := logrus.New()
|
||||
logger.Level = level
|
||||
|
||||
for _, hook := range DefaultHooks() {
|
||||
logger.Hooks.Add(hook)
|
||||
}
|
||||
|
||||
return logger
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/heptio/ark/pkg/cmd/util/flag"
|
||||
)
|
||||
|
||||
var sortedLogLevels = sortLogLevels()
|
||||
|
||||
// LevelFlag is a command-line flag for setting the logrus
|
||||
// log level.
|
||||
type LevelFlag struct {
|
||||
*flag.Enum
|
||||
defaultValue logrus.Level
|
||||
}
|
||||
|
||||
// LogLevelFlag constructs a new log level flag.
|
||||
func LogLevelFlag(defaultValue logrus.Level) *LevelFlag {
|
||||
return &LevelFlag{
|
||||
Enum: flag.NewEnum(defaultValue.String(), sortedLogLevels...),
|
||||
defaultValue: defaultValue,
|
||||
}
|
||||
}
|
||||
|
||||
// Parse returns the flag's value as a logrus.Level.
|
||||
func (f *LevelFlag) Parse() logrus.Level {
|
||||
if parsed, err := logrus.ParseLevel(f.String()); err == nil {
|
||||
return parsed
|
||||
}
|
||||
|
||||
// This should theoretically never happen assuming the enum flag
|
||||
// is constructed correctly because the enum flag will not allow
|
||||
// an invalid value to be set.
|
||||
logrus.Errorf("log-level flag has invalid value %s", strings.ToUpper(f.String()))
|
||||
return f.defaultValue
|
||||
}
|
||||
|
||||
// sortLogLevels returns a string slice containing all of the valid logrus
|
||||
// log levels (based on logrus.AllLevels), sorted in ascending order of severity.
|
||||
func sortLogLevels() []string {
|
||||
var (
|
||||
sortedLogLevels = make([]logrus.Level, len(logrus.AllLevels))
|
||||
logLevelsStrings []string
|
||||
)
|
||||
|
||||
copy(sortedLogLevels, logrus.AllLevels)
|
||||
|
||||
// logrus.Panic has the lowest value, so the compare function uses ">"
|
||||
sort.Slice(sortedLogLevels, func(i, j int) bool { return sortedLogLevels[i] > sortedLogLevels[j] })
|
||||
|
||||
for _, level := range sortedLogLevels {
|
||||
logLevelsStrings = append(logLevelsStrings, level.String())
|
||||
}
|
||||
|
||||
return logLevelsStrings
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
Copyright 2018 the Heptio Ark 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 sync
|
||||
|
||||
import "sync"
|
||||
|
||||
// An ErrorGroup waits for a collection of goroutines that return errors to finish.
|
||||
// The main goroutine calls Go one or more times to execute a function that returns
|
||||
// an error in a goroutine. Then it calls Wait to wait for all goroutines to finish
|
||||
// and collect the results of each.
|
||||
type ErrorGroup struct {
|
||||
wg sync.WaitGroup
|
||||
errChan chan error
|
||||
}
|
||||
|
||||
// Go runs the specified function in a goroutine.
|
||||
func (eg *ErrorGroup) Go(action func() error) {
|
||||
if eg.errChan == nil {
|
||||
eg.errChan = make(chan error)
|
||||
}
|
||||
|
||||
eg.wg.Add(1)
|
||||
go func() {
|
||||
eg.errChan <- action()
|
||||
eg.wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
// GoErrorSlice runs a function that returns a slice of errors
|
||||
// in a goroutine.
|
||||
func (eg *ErrorGroup) GoErrorSlice(action func() []error) {
|
||||
if eg.errChan == nil {
|
||||
eg.errChan = make(chan error)
|
||||
}
|
||||
|
||||
eg.wg.Add(1)
|
||||
go func() {
|
||||
for _, err := range action() {
|
||||
eg.errChan <- err
|
||||
}
|
||||
eg.wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
// Wait waits for all functions run via Go to finish,
|
||||
// and returns all of their errors.
|
||||
func (eg *ErrorGroup) Wait() []error {
|
||||
var errs []error
|
||||
go func() {
|
||||
for {
|
||||
errs = append(errs, <-eg.errChan)
|
||||
}
|
||||
}()
|
||||
|
||||
eg.wg.Wait()
|
||||
return errs
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
func UnstructuredOrDie(data string) *unstructured.Unstructured {
|
||||
o, _, err := unstructured.UnstructuredJSONScheme.Decode([]byte(data), nil, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return o.(*unstructured.Unstructured)
|
||||
}
|
||||
|
||||
func GetAsMap(j string) (map[string]interface{}, error) {
|
||||
m := make(map[string]interface{})
|
||||
err := json.Unmarshal([]byte(j), &m)
|
||||
return m, err
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/heptio/ark/pkg/apis/ark/v1"
|
||||
)
|
||||
|
||||
type MockPodCommandExecutor struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (e *MockPodCommandExecutor) ExecutePodCommand(log logrus.FieldLogger, item map[string]interface{}, namespace, name, hookName string, hook *v1.ExecHook) error {
|
||||
args := e.Called(log, item, namespace, name, hookName, hook)
|
||||
return args.Error(0)
|
||||
}
|
||||
Reference in New Issue
Block a user