mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-26 01:44:19 +00:00
* Modify according to comments. * Rename the pvSkipTracker and related fields to indicate both PVC and PV are supported. Signed-off-by: Xun Jiang <xun.jiang@broadcom.com>
160 lines
4.6 KiB
Go
160 lines
4.6 KiB
Go
/*
|
|
Copyright 2018 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 backup
|
|
|
|
import (
|
|
"sort"
|
|
"sync"
|
|
)
|
|
|
|
type SkippedVolume struct {
|
|
PVName string `json:"pvName"`
|
|
PVCName string `json:"pvcName,omitempty"`
|
|
PVCNamespace string `json:"pvcNamespace,omitempty"`
|
|
Reasons []Reason `json:"reasons"`
|
|
}
|
|
|
|
func (s *SkippedVolume) SerializeSkipReasons() string {
|
|
ret := ""
|
|
for _, reason := range s.Reasons {
|
|
ret = ret + reason.Approach + ": " + reason.Reason + ";"
|
|
}
|
|
return ret
|
|
}
|
|
|
|
type Reason struct {
|
|
Approach string `json:"approach"`
|
|
Reason string `json:"reason"`
|
|
}
|
|
|
|
// skipVolumeTracker keeps track of volumes (PV/PVC) that have been skipped and the reason why they are skipped.
|
|
type skipVolumeTracker struct {
|
|
*sync.RWMutex
|
|
// volumes is a map of volume(PV/PVC) key to the list of reasons why it is skipped.
|
|
// The reasons are stored in a map each key of the map is the backup approach, each approach can have one reason
|
|
volumes map[string]map[string]string
|
|
// includedVolumes is a set of volume key to be included in the backup, the element in this set should not be in the "volumes" map
|
|
includedVolumes map[string]struct{}
|
|
// volumeInfo is a map of volume key to SkippedVolume info
|
|
volumeInfo map[string]SkippedVolume
|
|
}
|
|
|
|
const (
|
|
podVolumeApproach = "podvolume"
|
|
csiSnapshotApproach = "csiSnapshot"
|
|
volumeSnapshotApproach = "volumeSnapshot"
|
|
vsphereSnapshotApproach = "vsphereSnapshot"
|
|
anyApproach = "any"
|
|
)
|
|
|
|
func NewSkipVolumeTracker() *skipVolumeTracker {
|
|
return &skipVolumeTracker{
|
|
RWMutex: &sync.RWMutex{},
|
|
volumes: make(map[string]map[string]string),
|
|
includedVolumes: make(map[string]struct{}),
|
|
volumeInfo: make(map[string]SkippedVolume),
|
|
}
|
|
}
|
|
|
|
func getVolumeKey(pvName, pvcName, pvcNamespace string) string {
|
|
if pvName != "" {
|
|
return "pv:" + pvName
|
|
}
|
|
if pvcName != "" && pvcNamespace != "" {
|
|
return "pvc:" + pvcNamespace + "/" + pvcName
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// Track tracks the Volume(PV/PVC) with the specified name and the reason why it is skipped
|
|
func (pt *skipVolumeTracker) Track(pvName, pvcName, pvcNamespace, approach, reason string) {
|
|
pt.Lock()
|
|
defer pt.Unlock()
|
|
key := getVolumeKey(pvName, pvcName, pvcNamespace)
|
|
if key == "" || reason == "" {
|
|
return
|
|
}
|
|
if _, ok := pt.includedVolumes[key]; ok {
|
|
return
|
|
}
|
|
skipReasons := pt.volumes[key]
|
|
if skipReasons == nil {
|
|
skipReasons = make(map[string]string)
|
|
pt.volumes[key] = skipReasons
|
|
}
|
|
if approach == "" {
|
|
approach = anyApproach
|
|
}
|
|
skipReasons[approach] = reason
|
|
pt.volumeInfo[key] = SkippedVolume{
|
|
PVName: pvName,
|
|
PVCName: pvcName,
|
|
PVCNamespace: pvcNamespace,
|
|
}
|
|
}
|
|
|
|
// Untrack removes the volume(pv/pvc) with the specified namespace and name.
|
|
// This func should be called when the volume is taken for snapshot, regardless native snapshot, CSI snapshot or fsb backup
|
|
// therefore, in one backup processed if a volume is Untracked once, it will not be tracked again.
|
|
func (pt *skipVolumeTracker) Untrack(pvName, pvcName, pvcNamespace string) {
|
|
pt.Lock()
|
|
defer pt.Unlock()
|
|
key := getVolumeKey(pvName, pvcName, pvcNamespace)
|
|
if key == "" {
|
|
return
|
|
}
|
|
pt.includedVolumes[key] = struct{}{}
|
|
delete(pt.volumes, key)
|
|
delete(pt.volumeInfo, key)
|
|
}
|
|
|
|
// Summary returns the summary of the tracked volumes.
|
|
func (pt *skipVolumeTracker) Summary() []SkippedVolume {
|
|
pt.RLock()
|
|
defer pt.RUnlock()
|
|
keys := make([]string, 0, len(pt.volumes))
|
|
for key := range pt.volumes {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
res := make([]SkippedVolume, 0, len(keys))
|
|
for _, key := range keys {
|
|
if skipReasons := pt.volumes[key]; len(skipReasons) > 0 {
|
|
info := pt.volumeInfo[key]
|
|
entry := SkippedVolume{
|
|
PVName: info.PVName,
|
|
PVCName: info.PVCName,
|
|
PVCNamespace: info.PVCNamespace,
|
|
Reasons: make([]Reason, 0, len(skipReasons)),
|
|
}
|
|
approaches := make([]string, 0, len(skipReasons))
|
|
for a := range skipReasons {
|
|
approaches = append(approaches, a)
|
|
}
|
|
sort.Strings(approaches)
|
|
for _, a := range approaches {
|
|
entry.Reasons = append(entry.Reasons, Reason{
|
|
Approach: a,
|
|
Reason: skipReasons[a],
|
|
})
|
|
}
|
|
res = append(res, entry)
|
|
}
|
|
}
|
|
return res
|
|
}
|