From c76373419c467ceb3bb0609309dbf8605cb177bf Mon Sep 17 00:00:00 2001 From: cheyang Date: Tue, 4 Aug 2026 16:41:58 +0800 Subject: [PATCH] fix(juicefs): read metaurl from SharedEncryptOptions when deciding the edition genEdition takes the dataset-wide encrypt options as its third argument, but transform passed the mount's own options instead: j.genEdition(dataset.Spec.Mounts[0], value, dataset.Spec.Mounts[0].EncryptOptions) so Mounts[0].EncryptOptions was scanned twice and Dataset.Spec.SharedEncryptOptions was never looked at. A dataset that declares metaurl only in SharedEncryptOptions was therefore classified as the enterprise edition. genValue does read SharedEncryptOptions, so such a dataset ended up with Source = "${METAURL}" while Edition = enterprise. That mismatch has two visible effects: - transformFuse takes the enterprise branch of genFormatCmd, where an empty TokenSecret makes it return early, so Configs.FormatCmd stays empty and the file system is never formatted. parseJuiceFSImage also picks the enterprise image for what is a community deployment. - On teardown, getUUID returns early for the enterprise edition with uuid = Source, so the literal string "${METAURL}" is joined into the cache path. cleanupCache then asks to remove "/${METAURL}/raw/chunks", which cmdguard rejects for containing `$`, so Shutdown fails and keeps retrying. Pass dataset.Spec.SharedEncryptOptions, which is what the parameter is named after. genEdition itself is unchanged: it already scans both lists correctly. The existing genEdition specs call the function directly with hand-built arguments, so they cover its logic but not how transform invokes it, which is where the defect was. Add a table that goes through transform and asserts the edition for metaurl declared on the mount, in SharedEncryptOptions, in both and in neither. Reverting the one-line change fails only the SharedEncryptOptions case, so the new coverage pins the call site rather than the helper. Signed-off-by: cheyang --- pkg/ddc/juicefs/transform.go | 2 +- pkg/ddc/juicefs/transform_test.go | 67 +++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/pkg/ddc/juicefs/transform.go b/pkg/ddc/juicefs/transform.go index eba30405263..1f98c4c9797 100644 --- a/pkg/ddc/juicefs/transform.go +++ b/pkg/ddc/juicefs/transform.go @@ -66,7 +66,7 @@ func (j *JuiceFSEngine) transform(runtime *datav1alpha1.JuiceFSRuntime) (value * } // generate edition - j.genEdition(dataset.Spec.Mounts[0], value, dataset.Spec.Mounts[0].EncryptOptions) + j.genEdition(dataset.Spec.Mounts[0], value, dataset.Spec.SharedEncryptOptions) // allocate ports err = j.allocatePorts(runtime, value) diff --git a/pkg/ddc/juicefs/transform_test.go b/pkg/ddc/juicefs/transform_test.go index 13096dd9f09..71da9aa5417 100644 --- a/pkg/ddc/juicefs/transform_test.go +++ b/pkg/ddc/juicefs/transform_test.go @@ -447,6 +447,73 @@ var _ = Describe("JuiceFSEngine Transform", func() { ) }) + // genEdition itself is covered below, but its arguments are supplied by transform. + // These cases go through transform so that the dataset-wide SharedEncryptOptions really + // reach it: declaring metaurl there means community edition just as much as declaring it + // on the mount does. + Describe("edition derived by transform", func() { + metaurlOption := []datav1alpha1.EncryptOption{{ + Name: JuiceMetaUrl, + ValueFrom: datav1alpha1.EncryptOptionSource{ + SecretKeyRef: datav1alpha1.SecretKeySelector{Name: "test", Key: "metaurl"}, + }, + }} + + BeforeEach(func() { + pr := net.ParsePortRangeOrDie("14000-15999") + Expect(portallocator.SetupRuntimePortAllocator(nil, pr, "bitmap", dummy)).To(Succeed()) + }) + + DescribeTable("should read metaurl from the whole dataset", + func(sharedEnc, mountEnc []datav1alpha1.EncryptOption, wantEdition string) { + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "fluid"}, + Data: map[string][]byte{"metaurl": []byte("redis://127.0.0.1:6379/0")}, + } + dataset := &datav1alpha1.Dataset{ + ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "fluid"}, + Spec: datav1alpha1.DatasetSpec{ + SharedEncryptOptions: sharedEnc, + Mounts: []datav1alpha1.Mount{{ + MountPoint: "juicefs:///mnt/test", + Name: "test", + EncryptOptions: mountEnc, + }}, + }, + } + fakeClient := fake.NewFakeClientWithScheme(testScheme, secret.DeepCopy(), dataset.DeepCopy()) + + runtimeInfo, err := base.BuildRuntimeInfo("test", "fluid", "juicefs") + Expect(err).NotTo(HaveOccurred()) + + jfsRuntime := &datav1alpha1.JuiceFSRuntime{ + ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "fluid"}, + Spec: datav1alpha1.JuiceFSRuntimeSpec{ + Fuse: datav1alpha1.JuiceFSFuseSpec{}, + Worker: datav1alpha1.JuiceFSCompTemplateSpec{Replicas: 1}, + }, + } + + engine := JuiceFSEngine{ + name: "test", + namespace: "fluid", + Client: fakeClient, + Log: fake.NullLogger(), + runtime: jfsRuntime, + runtimeInfo: runtimeInfo, + } + + value, err := engine.transform(jfsRuntime) + Expect(err).NotTo(HaveOccurred()) + Expect(value.Edition).To(Equal(wantEdition)) + }, + Entry("metaurl on the mount", nil, metaurlOption, CommunityEdition), + Entry("metaurl in SharedEncryptOptions", metaurlOption, nil, CommunityEdition), + Entry("metaurl in both", metaurlOption, metaurlOption, CommunityEdition), + Entry("metaurl nowhere", nil, nil, EnterpriseEdition), + ) + }) + Describe("genEdition", func() { type testArgs struct { mount datav1alpha1.Mount