Skip to content

Commit a9bc0c3

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]>
1 parent d1063bd commit a9bc0c3

File tree

11 files changed

+293
-39
lines changed

11 files changed

+293
-39
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
@@ -307,6 +307,60 @@ spec:
307307
are ANDed.
308308
type: object
309309
type: object
310+
labelSelectors:
311+
description: LabelSelectors is list of metav1.LabelSelector to filter
312+
with when adding individual objects to the backup. If empty or nil,
313+
all objects are included. Optional. If multiple provided they will
314+
be joined by the OR operator
315+
items:
316+
description: A label selector is a label query over a set of resources.
317+
The result of matchLabels and matchExpressions are ANDed. An empty
318+
label selector matches all objects. A null label selector matches
319+
no objects.
320+
properties:
321+
matchExpressions:
322+
description: matchExpressions is a list of label selector requirements.
323+
The requirements are ANDed.
324+
items:
325+
description: A label selector requirement is a selector that
326+
contains values, a key, and an operator that relates the
327+
key and values.
328+
properties:
329+
key:
330+
description: key is the label key that the selector applies
331+
to.
332+
type: string
333+
operator:
334+
description: operator represents a key's relationship
335+
to a set of values. Valid operators are In, NotIn, Exists
336+
and DoesNotExist.
337+
type: string
338+
values:
339+
description: values is an array of string values. If the
340+
operator is In or NotIn, the values array must be non-empty.
341+
If the operator is Exists or DoesNotExist, the values
342+
array must be empty. This array is replaced during a
343+
strategic merge patch.
344+
items:
345+
type: string
346+
type: array
347+
required:
348+
- key
349+
- operator
350+
type: object
351+
type: array
352+
matchLabels:
353+
additionalProperties:
354+
type: string
355+
description: matchLabels is a map of {key,value} pairs. A single
356+
{key,value} in the matchLabels map is equivalent to an element
357+
of matchExpressions, whose key field is "key", the operator
358+
is "In", and the values array contains only "value". The requirements
359+
are ANDed.
360+
type: object
361+
type: object
362+
nullable: true
363+
type: array
310364
metadata:
311365
properties:
312366
labels:

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

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,6 +1695,60 @@ spec:
16951695
are ANDed.
16961696
type: object
16971697
type: object
1698+
labelSelectors:
1699+
description: LabelSelectors is list of metav1.LabelSelector to filter
1700+
with when restoring individual objects from the backup. If empty
1701+
or nil, all objects are included. Optional. If multiple provided
1702+
they will be joined by the OR operator
1703+
items:
1704+
description: A label selector is a label query over a set of resources.
1705+
The result of matchLabels and matchExpressions are ANDed. An empty
1706+
label selector matches all objects. A null label selector matches
1707+
no objects.
1708+
properties:
1709+
matchExpressions:
1710+
description: matchExpressions is a list of label selector requirements.
1711+
The requirements are ANDed.
1712+
items:
1713+
description: A label selector requirement is a selector that
1714+
contains values, a key, and an operator that relates the
1715+
key and values.
1716+
properties:
1717+
key:
1718+
description: key is the label key that the selector applies
1719+
to.
1720+
type: string
1721+
operator:
1722+
description: operator represents a key's relationship
1723+
to a set of values. Valid operators are In, NotIn, Exists
1724+
and DoesNotExist.
1725+
type: string
1726+
values:
1727+
description: values is an array of string values. If the
1728+
operator is In or NotIn, the values array must be non-empty.
1729+
If the operator is Exists or DoesNotExist, the values
1730+
array must be empty. This array is replaced during a
1731+
strategic merge patch.
1732+
items:
1733+
type: string
1734+
type: array
1735+
required:
1736+
- key
1737+
- operator
1738+
type: object
1739+
type: array
1740+
matchLabels:
1741+
additionalProperties:
1742+
type: string
1743+
description: matchLabels is a map of {key,value} pairs. A single
1744+
{key,value} in the matchLabels map is equivalent to an element
1745+
of matchExpressions, whose key field is "key", the operator
1746+
is "In", and the values array contains only "value". The requirements
1747+
are ANDed.
1748+
type: object
1749+
type: object
1750+
nullable: true
1751+
type: array
16981752
namespaceMapping:
16991753
additionalProperties:
17001754
type: string

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

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

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+
// LabelSelectors 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+
LabelSelectors []*metav1.LabelSelector `json:"labelSelectors,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+
// LabelSelectors 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+
LabelSelectors []*metav1.LabelSelector `json:"labelSelectors,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: 55 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -296,50 +296,70 @@ func (r *itemCollector) getResourceItems(log logrus.FieldLogger, gv schema.Group
296296
labelSelector = metav1.FormatLabelSelector(selector)
297297
}
298298

299-
log.Info("Listing items")
300-
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
299+
var labelSelectors []string
300+
if r.backupRequest.Spec.LabelSelectors != nil {
301+
for _, s := range r.backupRequest.Spec.LabelSelectors {
302+
labelSelectors = append(labelSelectors, metav1.FormatLabelSelector(s))
332303
}
333304
} 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+
labelSelectors = []string{}
306+
}
307+
308+
log.Info("Listing items")
309+
unstructuredItems := make([]unstructured.Unstructured, 0)
310+
for _, label := range labelSelectors {
311+
unstructuredList, err := resourceClient.List(metav1.ListOptions{LabelSelector: label})
336312
if err != nil {
337313
log.WithError(errors.WithStack(err)).Error("Error listing items")
338314
continue
339315
}
340316
unstructuredItems = append(unstructuredItems, unstructuredList.Items...)
341317
}
342318

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

345365
// Collect items in included Namespaces

pkg/cmd/cli/backup/create.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ func (o *CreateOptions) BuildBackup(namespace string) (*velerov1api.Backup, erro
347347
if o.SnapshotVolumes.Value != nil {
348348
backupBuilder.SnapshotVolumes(*o.SnapshotVolumes.Value)
349349
}
350+
350351
if o.IncludeClusterResources.Value != nil {
351352
backupBuilder.IncludeClusterResources(*o.IncludeClusterResources.Value)
352353
}

0 commit comments

Comments
 (0)