v2.31.0-dev22 8d342c4

volume_targets.go RawHistoryFolder

1package backup
2
3import (
4	"sort"
5	"strings"
6
7	"github.com/bnema/gordon/internal/domain"
8)
9
10// SelectVolumeBackupTargets returns deterministic volume backup targets from Gordon-managed containers.
11func SelectVolumeBackupTargets(containers []*domain.Container, volumePrefix string) []domain.VolumeBackupTarget {
12	return SelectVolumeBackupTargetsForScope(containers, volumePrefix, "", "")
13}
14
15// SelectVolumeBackupTargetsForScope returns deterministic volume backup targets after applying domain/volume filters.
16func SelectVolumeBackupTargetsForScope(containers []*domain.Container, volumePrefix, domainFilter, volumeFilter string) []domain.VolumeBackupTarget {
17	targets := make([]domain.VolumeBackupTarget, 0)
18	seenVolumes := make(map[string]struct{})
19
20	for _, c := range sortedVolumeBackupContainers(containers) {
21		domainName, ok := selectedVolumeBackupContainerDomain(c, domainFilter)
22		if !ok {
23			continue
24		}
25		for _, mount := range selectedVolumeBackupMounts(c.VolumeMounts, volumePrefix, volumeFilter) {
26			if _, ok := seenVolumes[mount.Name]; ok {
27				continue
28			}
29			seenVolumes[mount.Name] = struct{}{}
30			targets = append(targets, domain.VolumeBackupTarget{
31				Domain:        domainName,
32				ContainerName: c.Name,
33				ContainerID:   c.ID,
34				VolumeName:    mount.Name,
35				MountPath:     mount.Destination,
36			})
37		}
38	}
39
40	return targets
41}
42
43func sortedVolumeBackupContainers(containers []*domain.Container) []*domain.Container {
44	sorted := make([]*domain.Container, 0, len(containers))
45	for _, c := range containers {
46		if c != nil {
47			sorted = append(sorted, c)
48		}
49	}
50	sort.Slice(sorted, func(i, j int) bool {
51		leftDomain := volumeBackupDomain(sorted[i])
52		rightDomain := volumeBackupDomain(sorted[j])
53		if leftDomain != rightDomain {
54			return leftDomain < rightDomain
55		}
56		return sorted[i].Name < sorted[j].Name
57	})
58	return sorted
59}
60
61func selectedVolumeBackupContainerDomain(c *domain.Container, domainFilter string) (string, bool) {
62	if c.Labels == nil || c.Labels[domain.LabelManaged] != "true" {
63		return "", false
64	}
65	domainName := volumeBackupDomain(c)
66	if domainName == "" || (domainFilter != "" && domainName != domainFilter) {
67		return "", false
68	}
69	return domainName, true
70}
71
72func selectedVolumeBackupMounts(mounts []domain.ContainerVolumeMount, volumePrefix, volumeFilter string) []domain.ContainerVolumeMount {
73	selected := append([]domain.ContainerVolumeMount(nil), mounts...)
74	sort.Slice(selected, func(i, j int) bool {
75		if selected[i].Name != selected[j].Name {
76			return selected[i].Name < selected[j].Name
77		}
78		return selected[i].Destination < selected[j].Destination
79	})
80	out := selected[:0]
81	for _, mount := range selected {
82		if volumeFilter != "" && mount.Name != volumeFilter {
83			continue
84		}
85		if !isEligibleVolumeMount(mount, volumePrefix) {
86			continue
87		}
88		out = append(out, mount)
89	}
90	return out
91}
92
93func volumeBackupDomain(c *domain.Container) string {
94	if c == nil || c.Labels == nil {
95		return ""
96	}
97	if c.Labels[domain.LabelAttachment] == "true" {
98		return c.Labels[domain.LabelAttachedTo]
99	}
100	if domainName := c.Labels[domain.LabelDomain]; domainName != "" {
101		return domainName
102	}
103	return c.Labels[domain.LabelRoute]
104}
105
106func isEligibleVolumeMount(mount domain.ContainerVolumeMount, volumePrefix string) bool {
107	if mount.Type != "volume" || mount.Name == "" || mount.Destination == "" {
108		return false
109	}
110	prefix := strings.TrimSpace(volumePrefix)
111	if prefix == "" {
112		return true
113	}
114	return strings.HasPrefix(mount.Name, prefix+"-")
115}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close