Skip to content

Commit 5060d9e

Browse files
authored
Update CRD and logic to enable Non Admin BSL enforcements (#236)
Signed-off-by: Michal Pryc <[email protected]>
1 parent 47e640d commit 5060d9e

13 files changed

+392
-115
lines changed

api/v1alpha1/nonadminbackupstoragelocationrequest_types.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,13 @@ const (
4646
// NonAdminBackupStorageLocationRequestSpec defines the desired state of NonAdminBackupStorageLocationRequest
4747
type NonAdminBackupStorageLocationRequestSpec struct {
4848
// approvalDecision is the decision of the cluster admin on the Requested NonAdminBackupStorageLocation creation.
49+
// The value may be set to either approve or reject.
4950
// +optional
5051
ApprovalDecision NonAdminBSLRequest `json:"approvalDecision,omitempty"`
5152
}
5253

53-
// NonAdminBackupStorageLocationRequestStatusInfo contains information of the related NonAdminBackupStorageLocation object.
54-
type NonAdminBackupStorageLocationRequestStatusInfo struct {
54+
// SourceNonAdminBSL contains information of the NonAdminBackupStorageLocation object that triggered NonAdminBSLRequest
55+
type SourceNonAdminBSL struct {
5556
// requestedSpec contains the requested Velero BackupStorageLocation spec from the NonAdminBackupStorageLocation
5657
// +optionl
5758
RequestedSpec *velerov1.BackupStorageLocationSpec `json:"requestedSpec"`
@@ -71,10 +72,11 @@ type NonAdminBackupStorageLocationRequestStatusInfo struct {
7172

7273
// NonAdminBackupStorageLocationRequestStatus defines the observed state of NonAdminBackupStorageLocationRequest
7374
type NonAdminBackupStorageLocationRequestStatus struct {
75+
// nonAdminBackupStorageLocation contains information of the NonAdminBackupStorageLocation object that triggered NonAdminBSLRequest
7476
// +optional
75-
NonAdminBackupStorageLocationRequestStatusInfo *NonAdminBackupStorageLocationRequestStatusInfo `json:"nonAdminBackupStorageLocation,omitempty"`
76-
// phase is a simple one high-level summary of the lifecycle of an NonAdminBackupStorageLocation.
77+
SourceNonAdminBSL *SourceNonAdminBSL `json:"nonAdminBackupStorageLocation,omitempty"`
7778

79+
// phase represents the current state of the NonAdminBSLRequest. It can be either Pending, Approved or Rejected.
7880
// +optional
7981
Phase NonAdminBSLRequestPhase `json:"phase,omitempty"`
8082
}

api/v1alpha1/zz_generated.deepcopy.go

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

cmd/main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ func main() {
200200
RequireApprovalForBSL: *dpaConfiguration.RequireApprovalForBSL,
201201
SyncPeriod: dpaConfiguration.BackupSyncPeriod.Duration,
202202
DefaultSyncPeriod: defaultSyncPeriod,
203+
EnforcedBslSpec: dpaConfiguration.EnforceBSLSpec,
203204
}).SetupWithManager(mgr); err != nil {
204205
setupLog.Error(err, "unable to setup NonAdminBackupStorageLocation controller with manager")
205206
os.Exit(1)
@@ -255,6 +256,7 @@ func getDPAConfiguration(restConfig *rest.Config, oadpNamespace string) (v1alpha
255256
},
256257
EnforceBackupSpec: &velerov1.BackupSpec{},
257258
EnforceRestoreSpec: &velerov1.RestoreSpec{},
259+
EnforceBSLSpec: &v1alpha1.EnforceBackupStorageLocationSpec{},
258260
RequireApprovalForBSL: ptr.To(false),
259261
}
260262
var defaultSyncPeriod *time.Duration
@@ -281,6 +283,9 @@ func getDPAConfiguration(restConfig *rest.Config, oadpNamespace string) (v1alpha
281283
if nonAdmin.EnforceRestoreSpec != nil {
282284
dpaConfiguration.EnforceRestoreSpec = nonAdmin.EnforceRestoreSpec
283285
}
286+
if nonAdmin.EnforceBSLSpec != nil {
287+
dpaConfiguration.EnforceBSLSpec = nonAdmin.EnforceBSLSpec
288+
}
284289
if nonAdmin.GarbageCollectionPeriod != nil {
285290
dpaConfiguration.GarbageCollectionPeriod.Duration = nonAdmin.GarbageCollectionPeriod.Duration
286291
}

config/crd/bases/oadp.openshift.io_nonadminbackupstoragelocationrequests.yaml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@ spec:
4444
state of NonAdminBackupStorageLocationRequest
4545
properties:
4646
approvalDecision:
47-
description: approvalDecision is the decision of the cluster admin
48-
on the Requested NonAdminBackupStorageLocation creation.
47+
description: |-
48+
approvalDecision is the decision of the cluster admin on the Requested NonAdminBackupStorageLocation creation.
49+
The value may be set to either approve or reject.
4950
enum:
5051
- approve
5152
- reject
@@ -57,8 +58,8 @@ spec:
5758
state of NonAdminBackupStorageLocationRequest
5859
properties:
5960
nonAdminBackupStorageLocation:
60-
description: NonAdminBackupStorageLocationRequestStatusInfo contains
61-
information of the related NonAdminBackupStorageLocation object.
61+
description: nonAdminBackupStorageLocation contains information of
62+
the NonAdminBackupStorageLocation object that triggered NonAdminBSLRequest
6263
properties:
6364
nacuuid:
6465
description: nacuuid references the NonAdminBackupStorageLocation
@@ -162,7 +163,8 @@ spec:
162163
- requestedSpec
163164
type: object
164165
phase:
165-
description: NonAdminBSLRequestPhase is the phase of the NonAdminBackupStorageLocationRequest
166+
description: phase represents the current state of the NonAdminBSLRequest.
167+
It can be either Pending, Approved or Rejected.
166168
enum:
167169
- Pending
168170
- Approved

docs/design/admin_control_over_spec.md

Lines changed: 67 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,47 @@
22

33
## Abstract
44

5-
Non Admin Controller (NAC) restricts the usage of OADP operator with NonAdminBackups and NonAdminRestores.
6-
Admin users may want to further restrict this by restricting NonAdminBackup/NonAdminRestore spec fields values.
5+
Non Admin Controller (NAC) restricts the usage of OADP operator with NonAdminBackups, NonAdminRestores and NonAdminBackupStorageLocations.
6+
Admin users may want to further restrict this by restricting NonAdminBackup/NonAdminRestore/NonAdminBackupStorageLocation spec fields values.
77

88
## Background
99

10-
Non Admin Controller (NAC) adds the ability to admin users restrict the use of OADP operator for non admin users, by only allowing them to create backup/restores from their namespaces with NonAdminBackups/NonAdminRestores.
10+
Non Admin Controller (NAC) adds the ability to admin users restrict the use of OADP operator for non admin users, by only allowing them to
11+
create backup/restore/backupstoragelocation objects from their namespaces with NonAdminBackups/NonAdminRestores/NonAdminBackupStorageLocations.
1112
Admin users may want to further restrict non admin users operations, like forcing a specific time to live (TTL) for NonAdminBackups associated Velero Backups.
12-
This design enables admin users to set custom default values for NonAdminBackup/NonAdminRestore spec fields, which can not be overridden by non-admin users.
13+
This design enables admin users to set custom default values for NonAdminBackup/NonAdminRestore/NonAdminBackupStorageLocation spec fields,
14+
which can not be overridden by non-admin users.
1315

1416
## Goals
1517

1618
Enable admin users to
1719
- set custom default values for NonAdminBackup spec.backupSpec fields, which can not be overridden
1820
- set custom default values for NonAdminRestore spec.restoreSpec fields, which can not be overridden
21+
- set custom default values for NonAdminBackupStorageLocation spec.backupStorageLocationSpec fields, which can not be overridden
1922

2023
Also
2124
- Show custom default values validation errors in NAC object statuses and in NAC logs
2225

2326
## Non Goals
2427

25-
- Show NonAdminBackup spec.backupSpec fields/NonAdminRestore spec.restoreSpec fields custom default values to non admin users
26-
- Prevent non admin users to create NonAdminBackup/NonAdminRestore with overridden defaults
28+
- Show the custom default values to non admin users in NonAdminBackup/NonAdminRestore/NonAdminBackupStorageLocation spec fields
29+
- Prevent non admin users to create NonAdminBackup/NonAdminRestore/NonAdminBackupStorageLocation with overridden defaults
2730
- Allow admin users to set second level defaults (for example, NonAdminBackup `spec.backupSpec.labelSelector` can have a custom default value, but not just `spec.backupSpec.labelSelector.matchLabels`)
2831
- Check if there are on-going NAC operations prior to recreating NAC Pod
29-
- Allow admin users to enforce falsy values (like empty maps or empty lists) for NonAdminBackup spec.backupSpec fields/NonAdminRestore spec.restoreSpec fields
32+
- Allow admin users to enforce falsy values (like empty maps or empty lists) for NonAdminBackup spec.backupSpec fields/NonAdminRestore spec.restoreSpec fields/NonAdminBackupStorageLocation spec.backupStorageLocationSpec fields
3033

3134
## High-Level Design
3235

33-
A field will be added to OADP DPA object. With it, admin users will be able to select which NonAdminBackup `spec.backupSpec` fields have custom default (and enforced) values. NAC will respect the set values. If a NonAdminBackup is created with fields overriding any enforced values, it will fail validation prior to creating an associated Velero Backup.
36+
New fields will be added to the OADP DPA object, allowing admin users to define custom default and enforced values for specific fields in NonAdminBackup, NonAdminRestore, and NonAdminBackupStorageLocation specifications. The NAC will enforce these values accordingly.
3437

35-
Another field will be added to OADP DPA object. With it, admin users will be able to select which NonAdminRestore `spec.restoreSpec` fields have custom default (and enforced) values. NAC will respect the set values. If a NonAdminRestore is created with fields overriding any enforced values, it will fail validation prior to creating an associated Velero Restore.
38+
- **NonAdminBackup:**
39+
Admin users can specify which `spec.backupSpec` fields have custom default and enforced values. If a NonAdminBackup is created with values that override enforced settings, it will fail validation before creating an associated Velero Backup.
40+
41+
- **NonAdminRestore:**
42+
Admin users can define enforced and default values for `spec.restoreSpec` fields. Any NonAdminRestore that attempts to override enforced values will fail validation before creating an associated Velero Restore.
43+
44+
- **NonAdminBackupStorageLocation:**
45+
Admin users can set enforced and default values for `spec.backupStorageLocationSpec` fields, except for spec.backupStorageLocationSpec.default, which is not included in the enforcement BSL Spec. If a NonAdminBackupStorageLocation attempts to override enforced values, it will fail validation before creating an associated Velero BackupStorageLocation.
3646

3747
If admin user changes any enforced field value, NAC Pod is recreated to always be up to date with admin user enforcements.
3848

@@ -42,7 +52,16 @@ If admin user changes any enforced field value, NAC Pod is recreated to always b
4252

4353
Field `spec.nonAdmin.enforceBackupSpec`, of the same type as the Velero Backup Spec, will be added to OADP DPA object.
4454

45-
With it, admin users will be able to select which NonAdminBackup `spec.backupSpec` fields have custom default (and enforced) values.
55+
Field `spec.nonAdmin.enforceRestoreSpec`, of the same type as the Velero Restore Spec, will be added to OADP DPA object.
56+
57+
Field `spec.nonAdmin.enforceBSLSpec`, which mirrors the Velero BackupStorageLocation Spec, will be introduced in the
58+
OADP DPA object with the following exceptions:
59+
60+
- Fields marked as `required` in the Velero BSL Spec are treated as `optional` in the enforcement BSL Spec.
61+
This allows admin users to enforce specific fields without requiring others.
62+
- The `default` field is excluded from the enforcement BSL Spec, because it can not be enforced.
63+
64+
With the above fields, admin users will be able to select for example which NonAdminBackup `spec.backupSpec` fields have custom default (and enforced) values.
4665

4766
To avoid mistakes, not all fields will be able to be enforced, like `IncludedNamespaces`, that could break NAC usage.
4867

@@ -65,8 +84,6 @@ spec:
6584
enable: true
6685
enforceBackupSpecs:
6786
snapshotVolumes: false
68-
unsupportedOverrides:
69-
tech-preview-ack: 'true'
7087
```
7188
7289
That means, that the 2 following NonAdminBackup will be accepted by NAC validation
@@ -123,48 +140,17 @@ Add `EnforceRestoreSpec` struct to OADP DPA `NonAdmin` struct
123140
type NonAdmin struct {
124141
// which restore spec field values to enforce
125142
// +optional
126-
EnforceBackupSpec *velero.BackupSpec `json:"enforceBackupSpec,omitempty"`
143+
EnforceRestoreSpec *velero.RestoreSpec `json:"enforceRestoreSpec,omitempty"`
127144
}
128145
```
129146

130-
Store previous `EnforceBackupSpec` and `EnforceRestoreSpec` value, so when admin user changes it, Deployment is also changed to trigger a Pod recreation
147+
Add `EnforceBSLSpec` struct to OADP DPA `NonAdmin` struct
131148
```go
132-
const (
133-
enforcedBackupSpecKey = "enforced-backup-spec"
134-
enforcedRestoreSpecKey = "enforced-restore-spec"
135-
)
136-
137-
var (
138-
previousEnforcedBackupSpec *velero.BackupSpec = nil
139-
dpaBackupSpecResourceVersion = ""
140-
previousEnforcedRestoreSpec *velero.RestoreSpec = nil
141-
dpaRestoreSpecResourceVersion = ""
142-
)
143-
144-
func ensureRequiredSpecs(deploymentObject *appsv1.Deployment, dpa *oadpv1alpha1.DataProtectionApplication, image string, imagePullPolicy corev1.PullPolicy) error {
145-
if len(dpaBackupSpecResourceVersion) == 0 || !reflect.DeepEqual(dpa.Spec.NonAdmin.EnforceBackupSpec, previousEnforcedBackupSpec) {
146-
dpaBackupSpecResourceVersion = dpa.GetResourceVersion()
147-
}
148-
previousEnforcedBackupSpec = dpa.Spec.NonAdmin.EnforceBackupSpec
149-
if len(dpaRestoreSpecResourceVersion) == 0 || !reflect.DeepEqual(dpa.Spec.NonAdmin.EnforceRestoreSpec, previousEnforcedRestoreSpec) {
150-
dpaRestoreSpecResourceVersion = dpa.GetResourceVersion()
151-
}
152-
previousEnforcedRestoreSpec = dpa.Spec.NonAdmin.EnforceRestoreSpec
153-
enforcedSpecAnnotation := map[string]string{
154-
enforcedBackupSpecKey: dpaBackupSpecResourceVersion,
155-
enforcedRestoreSpecKey: dpaRestoreSpecResourceVersion,
156-
}
157-
158-
templateObjectAnnotations := deploymentObject.Spec.Template.GetAnnotations()
159-
if templateObjectAnnotations == nil {
160-
deploymentObject.Spec.Template.SetAnnotations(enforcedSpecAnnotation)
161-
} else {
162-
templateObjectAnnotations[enforcedBackupSpecKey] = enforcedSpecAnnotation[enforcedBackupSpecKey]
163-
templateObjectAnnotations[enforcedRestoreSpecKey] = enforcedSpecAnnotation[enforcedRestoreSpecKey]
164-
deploymentObject.Spec.Template.SetAnnotations(templateObjectAnnotations)
165-
}
149+
type NonAdmin struct {
150+
// which backupstoragelocation spec field values to enforce
151+
// +optional
152+
EnforceBSLSpec *velero.BackupStorageLocationSpec `json:"enforceBSLSpec,omitempty"`
166153
}
167-
```
168154

169155
During NAC startup, read OADP DPA, to be able to apply admin user enforcement
170156
```go
@@ -220,7 +206,7 @@ Before creating NonAdminBackup's related Velero Backup, apply any missing fields
220206
}
221207
```
222208

223-
Modify ValidateRestoreSpec function to use `EnforceRestoreSpec` and apply that to non admin users' NonAdminBackup request
209+
Modify ValidateRestoreSpec function to use `EnforceRestoreSpec` and apply that to non admin users' NonAdminRestore request
224210
```go
225211
enforcedSpec := reflect.ValueOf(enforcedRestoreSpec).Elem()
226212
for index := range enforcedSpec.NumField() {
@@ -251,6 +237,37 @@ Before creating NonAdminRestore's related Velero Restore, apply any missing fiel
251237
}
252238
```
253239

240+
Modify ValidateBSLSpec function to use `EnforceBSLSpec` and apply that to non admin users' NonAdminBackupStorageLocation request
241+
```go
242+
enforcedSpec := reflect.ValueOf(enforcedBSLSpec).Elem()
243+
for index := range enforcedSpec.NumField() {
244+
enforcedField := enforcedSpec.Field(index)
245+
enforcedFieldName := enforcedSpec.Type().Field(index).Name
246+
currentField := reflect.ValueOf(nonAdminBsl.Spec.BackupStorageLocationSpec).Elem().FieldByName(enforcedFieldName)
247+
if !enforcedField.IsZero() && !currentField.IsZero() && !reflect.DeepEqual(enforcedField.Interface(), currentField.Interface()) {
248+
field, _ := reflect.TypeOf(nonAdminBsl.Spec.BackupStorageLocationSpec).Elem().FieldByName(enforcedFieldName)
249+
tagName, _, _ := strings.Cut(field.Tag.Get("json"), ",")
250+
return fmt.Errorf(
251+
"NonAdminBackupStorageLocation spec.backupStorageLocationSpec.%v field value is enforced by admin user, can not override it",
252+
tagName,
253+
)
254+
}
255+
}
256+
```
257+
258+
Before creating NonAdminBackupStorageLocation's related Velero BackupStorageLocation, apply any missing fields to it that admin user has enforced
259+
```go
260+
enforcedSpec := reflect.ValueOf(r.EnforcedBSLSpec).Elem()
261+
for index := range enforcedSpec.NumField() {
262+
enforcedField := enforcedSpec.Field(index)
263+
enforcedFieldName := enforcedSpec.Type().Field(index).Name
264+
currentField := reflect.ValueOf(bslSpec).Elem().FieldByName(enforcedFieldName)
265+
if !enforcedField.IsZero() && currentField.IsZero() {
266+
currentField.Set(enforcedField)
267+
}
268+
}
269+
```
270+
254271
For more details, check https://github.com/openshift/oadp-operator/pull/1584, https://github.com/migtools/oadp-non-admin/pull/110, https://github.com/openshift/oadp-operator/pull/1600 and https://github.com/migtools/oadp-non-admin/pull/122.
255272

256273
## Open Issues

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ require (
99
github.com/google/uuid v1.6.0
1010
github.com/onsi/ginkgo/v2 v2.19.0
1111
github.com/onsi/gomega v1.33.1
12-
github.com/openshift/oadp-operator v1.0.2-0.20250225171529-2e93d8609b8f
12+
github.com/openshift/oadp-operator v1.0.2-0.20250227152435-9267da21ab64
1313
github.com/sirupsen/logrus v1.9.3
1414
github.com/stretchr/testify v1.9.0
1515
github.com/vmware-tanzu/velero v1.14.0

0 commit comments

Comments
 (0)