Skip to content

Commit d64a975

Browse files
authored
fix: add DataSource reference handling to authorize utils (#3831)
* fix: add DataSource reference to authorize_utils A previous PR[1] introduced the concept of DataSource references as a way for a DataSource to reference another DataSource. A DV that has its sourceRef set to a DataSource reference that resides in a different namespace would fail as authorize utils did not include the necessary changes to the clone source handler to accomodate DataSource references. This commit fixes that and introduces a functional test for it. [1] #3760 Signed-off-by: Adi Aloni <[email protected]> * datasource-controller: prohibit cross-namespace references Previously a DataSource could reference a DataSource in a namespace different from its own. This is unwanted behavior and with this commit we now error when we create such DataSources. Signed-off-by: Adi Aloni <[email protected]> --------- Signed-off-by: Adi Aloni <[email protected]>
1 parent cd0364a commit d64a975

File tree

5 files changed

+80
-5
lines changed

5 files changed

+80
-5
lines changed

pkg/apiserver/webhooks/datavolume-mutate_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,62 @@ var _ = Describe("Mutating DataVolume Webhook", func() {
358358
Entry("succeed with same (default) namespace", "default"),
359359
Entry("succeed with empty namespace", ""),
360360
)
361+
362+
It("should allow update to DataVolume with sourceRef DataSource reference with clone token", func() {
363+
dsRef := &cdicorev1.DataSource{
364+
ObjectMeta: metav1.ObjectMeta{
365+
Name: "dsRef",
366+
Namespace: "testNamespace",
367+
},
368+
Spec: cdicorev1.DataSourceSpec{
369+
Source: cdicorev1.DataSourceSource{
370+
DataSource: &cdicorev1.DataSourceRefSourceDataSource{
371+
Namespace: "testNamespace",
372+
Name: "ds",
373+
},
374+
},
375+
},
376+
Status: cdicorev1.DataSourceStatus{
377+
Source: cdicorev1.DataSourceSource{
378+
PVC: &cdicorev1.DataVolumeSourcePVC{
379+
Namespace: "testNamespace",
380+
Name: "pvc",
381+
},
382+
},
383+
},
384+
}
385+
sourceRef := cdicorev1.DataVolumeSourceRef{
386+
Kind: cdicorev1.DataVolumeDataSource,
387+
Namespace: &dsRef.Namespace,
388+
Name: dsRef.Name,
389+
}
390+
dv := newDataVolumeWithSourceRef("dsRefDv", nil, &sourceRef, nil)
391+
dvBytes, _ := json.Marshal(&dv)
392+
ar := &admissionv1.AdmissionReview{
393+
Request: &admissionv1.AdmissionRequest{
394+
Operation: admissionv1.Create,
395+
Resource: metav1.GroupVersionResource{
396+
Group: cdicorev1.SchemeGroupVersion.Group,
397+
Version: cdicorev1.SchemeGroupVersion.Version,
398+
Resource: "datavolumes",
399+
},
400+
Object: runtime.RawExtension{
401+
Raw: dvBytes,
402+
},
403+
},
404+
}
405+
406+
resp := mutateDVs(key, ar, true, dsRef)
407+
Expect(resp.Allowed).To(BeTrue())
408+
Expect(resp.Patch).ToNot(BeNil())
409+
var patchObjs []jsonpatch.Operation
410+
err := json.Unmarshal(resp.Patch, &patchObjs)
411+
Expect(err).ToNot(HaveOccurred())
412+
Expect(patchObjs).Should(HaveLen(1))
413+
Expect(patchObjs[0].Operation).Should(Equal("add"))
414+
Expect(patchObjs[0].Path).Should(Equal("/metadata/annotations"))
415+
Expect(patchObjs[0].Value).Should(HaveKey(cc.AnnCloneToken))
416+
})
361417
})
362418
})
363419

pkg/controller/common/util.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ var (
396396

397397
ErrDataSourceMaxDepthReached = errors.New("DataSource reference chain exceeds maximum depth of 1")
398398
ErrDataSourceSelfReference = errors.New("DataSource cannot self-reference")
399+
ErrDataSourceCrossNamespace = errors.New("DataSource cannot reference a DataSource in another namespace")
399400
)
400401

401402
// FakeValidator is a fake token validator
@@ -2090,9 +2091,13 @@ func ResolveDataSourceChain(ctx context.Context, client client.Client, dataSourc
20902091

20912092
ref := dataSource.Spec.Source.DataSource
20922093
refNs := GetNamespace(ref.Namespace, dataSource.Namespace)
2094+
if dataSource.Namespace != refNs {
2095+
return dataSource, ErrDataSourceCrossNamespace
2096+
}
20932097
if ref.Name == dataSource.Name && refNs == dataSource.Namespace {
20942098
return nil, ErrDataSourceSelfReference
20952099
}
2100+
20962101
resolved := &cdiv1.DataSource{}
20972102
if err := client.Get(ctx, types.NamespacedName{Name: ref.Name, Namespace: refNs}, resolved); err != nil {
20982103
return nil, err

pkg/controller/datasource-controller.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ const (
6060
dataSourceControllerName = "datasource-controller"
6161
maxReferenceDepthReached = "MaxReferenceDepthReached"
6262
selfReference = "SelfReference"
63+
crossNamespaceReference = "CrossNamespaceReference"
6364
)
6465

6566
// Reconcile loop for DataSourceReconciler
@@ -181,6 +182,8 @@ func handleDataSourceRefError(dataSource *cdiv1.DataSource, err error) error {
181182
reason = maxReferenceDepthReached
182183
case errors.Is(err, cc.ErrDataSourceSelfReference):
183184
reason = selfReference
185+
case errors.Is(err, cc.ErrDataSourceCrossNamespace):
186+
reason = crossNamespaceReference
184187
case k8serrors.IsNotFound(err):
185188
reason = cc.NotFound
186189
default:

pkg/controller/datasource-controller_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,16 @@ var _ = Describe("All DataSource Tests", func() {
243243
Expect(dsPointer.Spec.Source.DataSource.Name).To(Equal(dsPointer.Name))
244244
Expect(dsPointer.Status.Source.DataSource).To(BeNil())
245245
})
246+
It("DataSource pointer should fail to resolve cross-namespace DataSource", func() {
247+
dsPointer := createDataSource(dsName + "-pointer")
248+
dsPointer.Spec.Source = cdiv1.DataSourceSource{DataSource: &cdiv1.DataSourceRefSourceDataSource{Namespace: "differentNamespace", Name: dsPointer.Name}}
249+
reconciler := createDataSourceReconciler(dsPointer)
250+
dsPointerKey := types.NamespacedName{Name: dsPointer.Name, Namespace: metav1.NamespaceDefault}
251+
verifyConditions("DataSource cross-namespace reference", false, crossNamespaceReference, dsPointer, reconciler)
252+
Expect(reconciler.client.Get(context.TODO(), dsPointerKey, dsPointer)).To(Succeed())
253+
Expect(dsPointer.Spec.Source.DataSource.Name).To(Equal(dsPointer.Name))
254+
Expect(dsPointer.Status.Source.DataSource).To(BeNil())
255+
})
246256
})
247257

248258
})

staging/src/kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1/authorize_utils.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,12 @@ func newCloneSourceHandler(dataVolume *DataVolume, dsGet dsGetFunc) (CloneSource
4545
if err != nil {
4646
return CloneSourceHandler{}, err
4747
}
48-
if dataSource.Spec.Source.PVC != nil {
49-
pvcSource = dataSource.Spec.Source.PVC
50-
} else if dataSource.Spec.Source.Snapshot != nil {
51-
snapshotSource = dataSource.Spec.Source.Snapshot
52-
}
48+
pvcSource = dataSource.Spec.Source.PVC
49+
snapshotSource = dataSource.Spec.Source.Snapshot
50+
if dataSource.Spec.Source.DataSource != nil {
51+
pvcSource = dataSource.Status.Source.PVC
52+
snapshotSource = dataSource.Status.Source.Snapshot
53+
}
5354
}
5455

5556
switch {

0 commit comments

Comments
 (0)