Skip to content

Commit 8011eae

Browse files
add multiple label selector support to backup API
Signed-off-by: Shubham Pampattiwar <[email protected]> remove backup CLI bits Signed-off-by: Shubham Pampattiwar <[email protected]> labelselectors spec option for velero restore Signed-off-by: Shubham Pampattiwar <[email protected]> add changelog file Signed-off-by: Shubham Pampattiwar <[email protected]> update spec name to OrLabelSelectors Signed-off-by: Shubham Pampattiwar <[email protected]> minor fixes Signed-off-by: Shubham Pampattiwar <[email protected]>
1 parent d1063bd commit 8011eae

File tree

10 files changed

+297
-42
lines changed

10 files changed

+297
-42
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add multiple label selector support to Velero Backup and Restore APIs

config/crd/v1/bases/velero.io_backups.yaml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,60 @@ spec:
314314
type: string
315315
type: object
316316
type: object
317+
orLabelSelectors:
318+
description: OrLabelSelectors is list of metav1.LabelSelector to filter
319+
with when adding individual objects to the backup. If empty or nil,
320+
all objects are included. Optional. If multiple provided they will
321+
be joined by the OR operator
322+
items:
323+
description: A label selector is a label query over a set of resources.
324+
The result of matchLabels and matchExpressions are ANDed. An empty
325+
label selector matches all objects. A null label selector matches
326+
no objects.
327+
properties:
328+
matchExpressions:
329+
description: matchExpressions is a list of label selector requirements.
330+
The requirements are ANDed.
331+
items:
332+
description: A label selector requirement is a selector that
333+
contains values, a key, and an operator that relates the
334+
key and values.
335+
properties:
336+
key:
337+
description: key is the label key that the selector applies
338+
to.
339+
type: string
340+
operator:
341+
description: operator represents a key's relationship
342+
to a set of values. Valid operators are In, NotIn, Exists
343+
and DoesNotExist.
344+
type: string
345+
values:
346+
description: values is an array of string values. If the
347+
operator is In or NotIn, the values array must be non-empty.
348+
If the operator is Exists or DoesNotExist, the values
349+
array must be empty. This array is replaced during a
350+
strategic merge patch.
351+
items:
352+
type: string
353+
type: array
354+
required:
355+
- key
356+
- operator
357+
type: object
358+
type: array
359+
matchLabels:
360+
additionalProperties:
361+
type: string
362+
description: matchLabels is a map of {key,value} pairs. A single
363+
{key,value} in the matchLabels map is equivalent to an element
364+
of matchExpressions, whose key field is "key", the operator
365+
is "In", and the values array contains only "value". The requirements
366+
are ANDed.
367+
type: object
368+
type: object
369+
nullable: true
370+
type: array
317371
orderedResources:
318372
additionalProperties:
319373
type: string

config/crd/v1/bases/velero.io_restores.yaml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,6 +1703,60 @@ spec:
17031703
included in the map will be restored into namespaces of the same
17041704
name.
17051705
type: object
1706+
orLabelSelectors:
1707+
description: OrLabelSelectors is list of metav1.LabelSelector to filter
1708+
with when restoring individual objects from the backup. If empty
1709+
or nil, all objects are included. Optional. If multiple provided
1710+
they will be joined by the OR operator
1711+
items:
1712+
description: A label selector is a label query over a set of resources.
1713+
The result of matchLabels and matchExpressions are ANDed. An empty
1714+
label selector matches all objects. A null label selector matches
1715+
no objects.
1716+
properties:
1717+
matchExpressions:
1718+
description: matchExpressions is a list of label selector requirements.
1719+
The requirements are ANDed.
1720+
items:
1721+
description: A label selector requirement is a selector that
1722+
contains values, a key, and an operator that relates the
1723+
key and values.
1724+
properties:
1725+
key:
1726+
description: key is the label key that the selector applies
1727+
to.
1728+
type: string
1729+
operator:
1730+
description: operator represents a key's relationship
1731+
to a set of values. Valid operators are In, NotIn, Exists
1732+
and DoesNotExist.
1733+
type: string
1734+
values:
1735+
description: values is an array of string values. If the
1736+
operator is In or NotIn, the values array must be non-empty.
1737+
If the operator is Exists or DoesNotExist, the values
1738+
array must be empty. This array is replaced during a
1739+
strategic merge patch.
1740+
items:
1741+
type: string
1742+
type: array
1743+
required:
1744+
- key
1745+
- operator
1746+
type: object
1747+
type: array
1748+
matchLabels:
1749+
additionalProperties:
1750+
type: string
1751+
description: matchLabels is a map of {key,value} pairs. A single
1752+
{key,value} in the matchLabels map is equivalent to an element
1753+
of matchExpressions, whose key field is "key", the operator
1754+
is "In", and the values array contains only "value". The requirements
1755+
are ANDed.
1756+
type: object
1757+
type: object
1758+
nullable: true
1759+
type: array
17061760
preserveNodePorts:
17071761
description: PreserveNodePorts specifies whether to restore old nodePorts
17081762
from backup.

config/crd/v1/bases/velero.io_schedules.yaml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,60 @@ spec:
327327
type: string
328328
type: object
329329
type: object
330+
orLabelSelectors:
331+
description: OrLabelSelectors is list of metav1.LabelSelector
332+
to filter with when adding individual objects to the backup.
333+
If empty or nil, all objects are included. Optional. If multiple
334+
provided they will be joined by the OR operator
335+
items:
336+
description: A label selector is a label query over a set of
337+
resources. The result of matchLabels and matchExpressions
338+
are ANDed. An empty label selector matches all objects. A
339+
null label selector matches no objects.
340+
properties:
341+
matchExpressions:
342+
description: matchExpressions is a list of label selector
343+
requirements. The requirements are ANDed.
344+
items:
345+
description: A label selector requirement is a selector
346+
that contains values, a key, and an operator that relates
347+
the key and values.
348+
properties:
349+
key:
350+
description: key is the label key that the selector
351+
applies to.
352+
type: string
353+
operator:
354+
description: operator represents a key's relationship
355+
to a set of values. Valid operators are In, NotIn,
356+
Exists and DoesNotExist.
357+
type: string
358+
values:
359+
description: values is an array of string values.
360+
If the operator is In or NotIn, the values array
361+
must be non-empty. If the operator is Exists or
362+
DoesNotExist, the values array must be empty. This
363+
array is replaced during a strategic merge patch.
364+
items:
365+
type: string
366+
type: array
367+
required:
368+
- key
369+
- operator
370+
type: object
371+
type: array
372+
matchLabels:
373+
additionalProperties:
374+
type: string
375+
description: matchLabels is a map of {key,value} pairs.
376+
A single {key,value} in the matchLabels map is equivalent
377+
to an element of matchExpressions, whose key field is
378+
"key", the operator is "In", and the values array contains
379+
only "value". The requirements are ANDed.
380+
type: object
381+
type: object
382+
nullable: true
383+
type: array
330384
orderedResources:
331385
additionalProperties:
332386
type: string

config/crd/v1/crds/crds.go

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/velero/v1/backup.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ type BackupSpec struct {
5959
// +nullable
6060
LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"`
6161

62+
// OrLabelSelectors is list of metav1.LabelSelector to filter with
63+
// when adding individual objects to the backup. If empty
64+
// or nil, all objects are included. Optional. If multiple provided
65+
// they will be joined by the OR operator
66+
// +optional
67+
// +nullable
68+
OrLabelSelectors []*metav1.LabelSelector `json:"orLabelSelectors,omitempty"`
69+
6270
// SnapshotVolumes specifies whether to take cloud snapshots
6371
// of any PV's referenced in the set of objects included
6472
// in the Backup.

pkg/apis/velero/v1/restore.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ type RestoreSpec struct {
7171
// +nullable
7272
LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"`
7373

74+
// OrLabelSelectors is list of metav1.LabelSelector to filter with
75+
// when restoring individual objects from the backup. If empty
76+
// or nil, all objects are included. Optional. If multiple provided
77+
// they will be joined by the OR operator
78+
// +optional
79+
// +nullable
80+
OrLabelSelectors []*metav1.LabelSelector `json:"orLabelSelectors,omitempty"`
81+
7482
// RestorePVs specifies whether to restore all included
7583
// PVs from snapshot (via the cloudprovider).
7684
// +optional

pkg/apis/velero/v1/zz_generated.deepcopy.go

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/backup/item_collector.go

Lines changed: 60 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -291,55 +291,77 @@ func (r *itemCollector) getResourceItems(log logrus.FieldLogger, gv schema.Group
291291
continue
292292
}
293293

294-
var labelSelector string
295-
if selector := r.backupRequest.Spec.LabelSelector; selector != nil {
296-
labelSelector = metav1.FormatLabelSelector(selector)
294+
var orLabelSelectors []string
295+
if r.backupRequest.Spec.OrLabelSelectors != nil {
296+
for _, s := range r.backupRequest.Spec.OrLabelSelectors {
297+
orLabelSelectors = append(orLabelSelectors, metav1.FormatLabelSelector(s))
298+
}
299+
} else {
300+
orLabelSelectors = []string{}
297301
}
298302

299303
log.Info("Listing items")
300304
unstructuredItems := make([]unstructured.Unstructured, 0)
301-
302-
if r.pageSize > 0 {
303-
// If limit is positive, use a pager to split list over multiple requests
304-
// Use Velero's dynamic list function instead of the default
305-
listPager := pager.New(pager.SimplePageFunc(func(opts metav1.ListOptions) (runtime.Object, error) {
306-
return resourceClient.List(opts)
307-
}))
308-
// Use the page size defined in the server config
309-
// TODO allow configuration of page buffer size
310-
listPager.PageSize = int64(r.pageSize)
311-
// Add each item to temporary slice
312-
list, paginated, err := listPager.List(context.Background(), metav1.ListOptions{LabelSelector: labelSelector})
313-
if err != nil {
314-
log.WithError(errors.WithStack(err)).Error("Error listing resources")
315-
continue
316-
}
317-
if !paginated {
318-
log.Infof("list for groupResource %s was not paginated", gr)
319-
}
320-
err = meta.EachListItem(list, func(object runtime.Object) error {
321-
u, ok := object.(*unstructured.Unstructured)
322-
if !ok {
323-
log.WithError(errors.WithStack(fmt.Errorf("expected *unstructured.Unstructured but got %T", u))).Error("unable to understand entry in the list")
324-
return fmt.Errorf("expected *unstructured.Unstructured but got %T", u)
325-
}
326-
unstructuredItems = append(unstructuredItems, *u)
327-
return nil
328-
})
329-
if err != nil {
330-
log.WithError(errors.WithStack(err)).Error("unable to understand paginated list")
331-
continue
332-
}
333-
} else {
334-
// If limit is not positive, do not use paging. Instead, request all items at once
335-
unstructuredList, err := resourceClient.List(metav1.ListOptions{LabelSelector: labelSelector})
305+
for _, label := range orLabelSelectors {
306+
unstructuredList, err := resourceClient.List(metav1.ListOptions{LabelSelector: label})
336307
if err != nil {
337308
log.WithError(errors.WithStack(err)).Error("Error listing items")
338309
continue
339310
}
340311
unstructuredItems = append(unstructuredItems, unstructuredList.Items...)
341312
}
342313

314+
var labelSelector string
315+
if selector := r.backupRequest.Spec.LabelSelector; selector != nil {
316+
labelSelector = metav1.FormatLabelSelector(selector)
317+
}
318+
319+
// use labelSelector (singular) behavior only when the user has not specified OrLabelSelectors in backup API
320+
// this is by design because using both labelSelector and orLabelSelector do not have a real world use case
321+
// the AND use case is already taken care by the labelSelector (singular)
322+
if len(orLabelSelectors) == 0 {
323+
if r.pageSize > 0 {
324+
// If limit is positive, use a pager to split list over multiple requests
325+
// Use Velero's dynamic list function instead of the default
326+
listPager := pager.New(pager.SimplePageFunc(func(opts metav1.ListOptions) (runtime.Object, error) {
327+
return resourceClient.List(opts)
328+
}))
329+
// Use the page size defined in the server config
330+
// TODO allow configuration of page buffer size
331+
listPager.PageSize = int64(r.pageSize)
332+
// Add each item to temporary slice
333+
list, paginated, err := listPager.List(context.Background(), metav1.ListOptions{LabelSelector: labelSelector})
334+
if err != nil {
335+
log.WithError(errors.WithStack(err)).Error("Error listing resources")
336+
continue
337+
}
338+
if !paginated {
339+
log.Infof("list for groupResource %s was not paginated", gr)
340+
}
341+
err = meta.EachListItem(list, func(object runtime.Object) error {
342+
u, ok := object.(*unstructured.Unstructured)
343+
if !ok {
344+
log.WithError(errors.WithStack(fmt.Errorf("expected *unstructured.Unstructured but got %T", u))).Error("unable to understand entry in the list")
345+
return fmt.Errorf("expected *unstructured.Unstructured but got %T", u)
346+
}
347+
unstructuredItems = append(unstructuredItems, *u)
348+
return nil
349+
})
350+
if err != nil {
351+
log.WithError(errors.WithStack(err)).Error("unable to understand paginated list")
352+
continue
353+
}
354+
} else {
355+
// If limit is not positive, do not use paging. Instead, request all items at once
356+
unstructuredList, err := resourceClient.List(metav1.ListOptions{LabelSelector: labelSelector})
357+
if err != nil {
358+
log.WithError(errors.WithStack(err)).Error("Error listing items")
359+
continue
360+
}
361+
unstructuredItems = append(unstructuredItems, unstructuredList.Items...)
362+
}
363+
}
364+
343365
log.Infof("Retrieved %d items", len(unstructuredItems))
344366

345367
// Collect items in included Namespaces

0 commit comments

Comments
 (0)