-
Notifications
You must be signed in to change notification settings - Fork 1.3k
fix: populate Status.Selector in CacheEngine for worker pod discovery #6064
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
547bdfd
1901b8c
de65c86
df50f87
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -149,7 +149,7 @@ func (e *CacheEngine) CheckAndUpdateRuntimeStatus(value *common.CacheRuntimeStat | |
| runtimeToUpdate.Status.SetupDuration = utils.CalculateDuration(runtimeToUpdate.CreationTimestamp.Time, time.Now()) | ||
| } | ||
|
|
||
| // TODO(cache runtime): set the CacheRuntime Status left fields: Selector | ||
| runtimeToUpdate.Status.Selector = e.getWorkerSelectors() | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The selector itself looks right — One caveat worth flagging: as it stands, populating Could you add the |
||
| runtimeToUpdate.Status.ValueFile = common.GetCacheRuntimeConfigConfigMapName(e.name) | ||
|
|
||
| if !reflect.DeepEqual(runtime.Status, runtimeToUpdate.Status) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ import ( | |
|
|
||
| "github.com/fluid-cloudnative/fluid/pkg/common" | ||
| "github.com/fluid-cloudnative/fluid/pkg/utils" | ||
| "k8s.io/apimachinery/pkg/labels" | ||
| "k8s.io/apimachinery/pkg/util/validation" | ||
| ) | ||
|
|
||
|
|
@@ -129,3 +130,15 @@ func GetEmptyDirTieredStoreMountPath(levelIndex int) string { | |
| func getTieredStoreMountPath(levelIndex int, pathIndex int, mediumType string) string { | ||
| return fmt.Sprintf("/etc/fluid/mount/tiered-store/level-%d-index-%d-%s", levelIndex, pathIndex, mediumType) | ||
| } | ||
|
|
||
| // getWorkerSelectors returns the label selector string for CacheRuntime worker pods. | ||
| // This is used to populate Status.Selector so that HPA and other tooling can | ||
| // discover worker pods, matching the pattern used by other runtimes (JindoCache, | ||
| // JuiceFS, Vineyard, EFC). | ||
| func (e *CacheEngine) getWorkerSelectors() string { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This PR still ships without an e2e test for the new behavior, and that was called out as the merge blocker last round. Please add a focused e2e case (patterned after
Note: the earlier review pointed at |
||
| workerName := common.GetCacheComponentName(e.name, common.ComponentTypeWorker) | ||
| return labels.SelectorFromSet(labels.Set{ | ||
| common.LabelCacheRuntimeName: e.name, | ||
| common.LabelCacheRuntimeComponentName: workerName, | ||
| }).String() | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,8 @@ package engine | |
| import ( | ||
| "strings" | ||
|
|
||
| "github.com/fluid-cloudnative/fluid/pkg/common" | ||
| "github.com/fluid-cloudnative/fluid/pkg/utils/fake" | ||
| . "github.com/onsi/ginkgo/v2" | ||
| . "github.com/onsi/gomega" | ||
| "k8s.io/apimachinery/pkg/util/validation" | ||
|
|
@@ -194,3 +196,51 @@ var _ = Describe("getSecretVolumeName Tests", Label("pkg.ddc.cache.engine.util_t | |
| }) | ||
| }) | ||
| }) | ||
|
|
||
| var _ = Describe("getWorkerSelectors Tests", Label("pkg.ddc.cache.engine.util_test.go"), func() { | ||
| Describe("getWorkerSelectors", func() { | ||
| var engine *CacheEngine | ||
|
|
||
| BeforeEach(func() { | ||
| engine = &CacheEngine{ | ||
| name: "test-runtime", | ||
| namespace: "default", | ||
| Log: fake.NullLogger(), | ||
| } | ||
| }) | ||
|
|
||
| It("returns a non-empty selector string", func() { | ||
| selector := engine.getWorkerSelectors() | ||
| Expect(selector).NotTo(BeEmpty()) | ||
| }) | ||
|
|
||
| It("selector contains the runtime name label", func() { | ||
| selector := engine.getWorkerSelectors() | ||
| Expect(selector).To(ContainSubstring(common.LabelCacheRuntimeName)) | ||
| Expect(selector).To(ContainSubstring("test-runtime")) | ||
| }) | ||
|
|
||
| It("selector contains the worker component name label", func() { | ||
| workerName := common.GetCacheComponentName("test-runtime", common.ComponentTypeWorker) | ||
| selector := engine.getWorkerSelectors() | ||
| Expect(selector).To(ContainSubstring(common.LabelCacheRuntimeComponentName)) | ||
| Expect(selector).To(ContainSubstring(workerName)) | ||
| }) | ||
|
|
||
| It("returns different selectors for different runtime names", func() { | ||
| engine1 := &CacheEngine{name: "runtime-a", namespace: "default", Log: fake.NullLogger()} | ||
| engine2 := &CacheEngine{name: "runtime-b", namespace: "default", Log: fake.NullLogger()} | ||
| Expect(engine1.getWorkerSelectors()).NotTo(Equal(engine2.getWorkerSelectors())) | ||
| }) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding one assertion that pins the exact selector string (e.g. |
||
|
|
||
| It("returns the exact expected selector string", func() { | ||
| // Pin the selector string so any future change to label keys, ordering, | ||
| // or escaping is caught immediately. | ||
| engine := &CacheEngine{name: "test-runtime", namespace: "default", Log: fake.NullLogger()} | ||
| workerName := common.GetCacheComponentName("test-runtime", common.ComponentTypeWorker) | ||
| expected := common.LabelCacheRuntimeComponentName + "=" + workerName + | ||
| "," + common.LabelCacheRuntimeName + "=test-runtime" | ||
| Expect(engine.getWorkerSelectors()).To(Equal(expected)) | ||
| }) | ||
| }) | ||
| }) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The new unit tests are tight — the exact-string pin in particular is a good addition. What's still missing is an e2e case exercising the field on a real CR. See the blocking issue for the concrete asks. The unit suite alone cannot detect, for example, a controller-side bug where |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| apiVersion: data.fluid.io/v1alpha1 | ||
| kind: CacheRuntime | ||
| metadata: | ||
| name: selector-demo | ||
| spec: | ||
| runtimeClassName: selector-demo | ||
| master: | ||
| # 如何区分 master 和 journal 的配置,两个在一个进程中(前缀,交给Curvine自行处理) | ||
| options: # https://curvineio.github.io/zh-cn/docs/Deploy/Deploy-Curvine-Cluster/Distributed-Mode/conf#master%E9%85%8D%E7%BD%AE%E9%A1%B9 | ||
| key1: master-value1 | ||
| replicas: 1 | ||
| worker: | ||
| options: # https://curvineio.github.io/zh-cn/docs/Deploy/Deploy-Curvine-Cluster/Distributed-Mode/conf#worker%E9%85%8D%E7%BD%AE%E9%A1%B9 | ||
| key1: worker-value1 | ||
| replicas: 1 | ||
| tieredStore: | ||
| levels: #worker缓存配置 | ||
| - low: "0.5" | ||
| high: "0.8" | ||
| emptyDir: | ||
| quota: 1Gi | ||
| client: | ||
| options: | ||
| key1: value1 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,210 @@ | ||
| apiVersion: data.fluid.io/v1alpha1 | ||
| kind: CacheRuntimeClass | ||
| metadata: | ||
| name: selector-demo | ||
| fileSystemType: curvinefs | ||
| extraResources: | ||
| configMaps: | ||
| - name: curvine-config | ||
| data: | ||
| # TOML格式配置模板, 使用 hairyhenderson/gomplate 镜像要求的模板格式 | ||
| cluster.toml: | | ||
| # master configuration | ||
| [master] | ||
| meta_dir = "testing/meta" | ||
|
|
||
| # masta ha raft configuration. | ||
| [journal] | ||
| journal_addrs = [ | ||
| {{ range $index := seq 0 (sub (ds "config").master.replicas 1) }} | ||
| {{- if $index }},{{ end }} | ||
| {id = {{ add $index 1 }}, hostname = "{{(ds "config").master.name}}-{{$index}}.{{(ds "config").master.service.name}}", port = 8996} | ||
| {{- end }} | ||
| ] | ||
| journal_dir = "testing/journal" | ||
|
|
||
| # Worker configuration, translate Gi to GB | ||
| [worker] | ||
| dir_reserved = "0" | ||
| data_dir = [ | ||
| {{- range $index, $level := (ds "config").worker.tieredStoreLevels }} | ||
| {{- range $pathIndex, $mountPath := $level.mountPaths }} | ||
| {{- if or $index $pathIndex }},{{ end }} | ||
| "[{{$level.mediumType}}:{{ index $level.quotas $pathIndex | strings.ReplaceAll "i" "B"}}]{{ $mountPath }}" | ||
| {{- end }} | ||
| {{- end }} | ||
| ] | ||
| dataOperationSpecs: | ||
| - name: DataLoad | ||
| command: | ||
| - "/bin/bash" | ||
| - "-c" | ||
| args: | ||
| # Actually, the cache runtime image should use $(FLUID_RUNTIME_CONFIG_PATH) to generate the config file, and | ||
| # use $(FLUID_DATALOAD_DATA_PATH) to execute data load. | ||
| - | | ||
| # currently we have no customized image supporting dataload for curvine, so we write the curvine.toml with fixed journal address for test case. | ||
| echo -e '[journal]\njournal_addrs = [\n{id=1, hostname="selector-demo-master-0.svc-selector-demo-master"}\n]' > /etc/curvine.toml | ||
|
|
||
| IFS=: read -ra paths <<< "$FLUID_DATALOAD_DATA_PATH" | ||
| for p in "${paths[@]}"; do | ||
| /app/curvine/bin/cv load "$p" --watch --conf /etc/curvine.toml || { | ||
| echo "Error: load $p failed." | ||
| exit 1 | ||
| } | ||
| done | ||
| topology: | ||
| master: | ||
| service: #需要为master创建Headless Service | ||
| headless: { } | ||
| dependencies: | ||
| extraResources: | ||
| # 使用 extraResources 时,需要定义其挂载路径 | ||
| configMaps: | ||
| - name: curvine-config | ||
| mountPath: "/templates" | ||
| executionEntries: | ||
| mountUFS: | ||
| command: | ||
| - bash | ||
| - "-c" | ||
| - "/etc/curvine/mount/mountUfs.sh" | ||
| timeout: 30 | ||
| template: | ||
| spec: | ||
| restartPolicy: Always | ||
| # 根据 runtime 生成的配置文件 | ||
| initContainers: | ||
| - name: init-curvine | ||
| image: hairyhenderson/gomplate:alpine | ||
| # 挂载共享卷到容器内路径 | ||
| volumeMounts: | ||
| - name: shared-config-volume | ||
| mountPath: /etc/curvine # 配置文件存放目录 | ||
| command: [ "gomplate" ] | ||
| args: [ "-d", "config=$(FLUID_RUNTIME_CONFIG_PATH)", "-f", "/templates/cluster.toml", "-o", "/etc/curvine/curvine.toml" ] | ||
| containers: | ||
| - name: master | ||
| image: "curvine/curvine:latest" | ||
| command: | ||
| - /entrypoint.sh | ||
| args: | ||
| - master | ||
| - start | ||
| env: | ||
| # /entrypoint.sh 不支持参数指定配置文件,支撑环境变量配置,默认/app/curvine/conf/curvine-cluster.toml | ||
| - name: CURVINE_CONF_FILE | ||
| value: "/etc/curvine/curvine.toml" | ||
| - name: POD_NAME | ||
| valueFrom: | ||
| fieldRef: | ||
| fieldPath: metadata.name | ||
| # curvine checks master hostname which should be one of journal_addrs | ||
| - name: CURVINE_MASTER_HOSTNAME | ||
| value: "$(POD_NAME).$(FLUID_RUNTIME_COMPONENT_SVC_NAME)" | ||
| volumeMounts: | ||
| - name: shared-config-volume | ||
| mountPath: /etc/curvine # 配置文件存放目录 | ||
| - name: curvine-mount-volume | ||
| mountPath: /etc/curvine/mount # 配置文件存放目录 | ||
| imagePullPolicy: IfNotPresent | ||
| volumes: | ||
| # emptyDir 共享存储(init容器和主容器互通) | ||
| - name: shared-config-volume | ||
| emptyDir: { } | ||
| - name: curvine-mount-volume | ||
| configMap: | ||
| name: curvine-mount | ||
| defaultMode: 0755 | ||
| worker: | ||
| service: | ||
| headless: { } #需要为worker创建Headless Service | ||
| dependencies: | ||
| extraResources: | ||
| # 使用 extraResources 时,需要定义其挂载路径 | ||
| configMaps: | ||
| - name: curvine-config | ||
| mountPath: "/templates" | ||
| template: | ||
| spec: | ||
| restartPolicy: Always | ||
| # 根据 runtime 生成的配置文件 | ||
| initContainers: | ||
| - name: init-curvine | ||
| image: hairyhenderson/gomplate:alpine | ||
| # 挂载共享卷到容器内路径 | ||
| volumeMounts: | ||
| - name: shared-config-volume | ||
| mountPath: /etc/curvine # 配置文件存放目录 | ||
| command: [ "gomplate" ] | ||
| args: [ "-d", "config=$(FLUID_RUNTIME_CONFIG_PATH)", "-f", "/templates/cluster.toml", "-o", "/etc/curvine/curvine.toml" ] | ||
| containers: | ||
| - name: worker | ||
| image: "curvine/curvine:latest" | ||
| command: | ||
| - /entrypoint.sh | ||
| args: | ||
| - worker | ||
| - start | ||
| env: | ||
| # /entrypoint.sh 不支持参数指定配置文件,支撑环境变量配置,默认/app/curvine/conf/curvine-cluster.toml | ||
| - name: CURVINE_CONF_FILE | ||
| value: "/etc/curvine/curvine.toml" | ||
| - name: POD_NAME | ||
| valueFrom: | ||
| fieldRef: | ||
| fieldPath: metadata.name | ||
| - name: CURVINE_WORKER_HOSTNAME | ||
| value: "$(POD_NAME).$(FLUID_RUNTIME_COMPONENT_SVC_NAME)" | ||
| volumeMounts: | ||
| - name: shared-config-volume | ||
| mountPath: /etc/curvine # 配置文件存放目录 | ||
| imagePullPolicy: IfNotPresent | ||
| volumes: | ||
| # emptyDir 共享存储(init容器和主容器互通) | ||
| - name: shared-config-volume | ||
| emptyDir: { } | ||
| client: | ||
| dependencies: | ||
| # Uncomment to enable secret mount for client (e.g., for JuiceFS FUSE pods) | ||
| # secretMount: | ||
| # enabled: true | ||
| extraResources: | ||
| # 使用 extraResources 时,需要定义其挂载路径 | ||
| configMaps: | ||
| - name: curvine-config | ||
| mountPath: "/templates" | ||
| template: | ||
| spec: | ||
| restartPolicy: Always | ||
| # 根据 runtime 生成的配置文件 | ||
| initContainers: | ||
| - name: init-curvine | ||
| image: hairyhenderson/gomplate:alpine | ||
| # 挂载共享卷到容器内路径 | ||
| volumeMounts: | ||
| - name: shared-config-volume | ||
| mountPath: /etc/curvine # 配置文件存放目录 | ||
| command: [ "gomplate" ] | ||
| args: [ "-d", "config=$(FLUID_RUNTIME_CONFIG_PATH)", "-f", "/templates/cluster.toml", "-o", "/etc/curvine/curvine.toml" ] | ||
| containers: | ||
| - name: client | ||
| image: "curvine/curvine:latest" | ||
| securityContext: #通常client需要配置privileged,用于操作fuse设备 | ||
| privileged: true | ||
| runAsUser: 0 | ||
| command: | ||
| - /app/curvine/lib/curvine-fuse | ||
| args: | ||
| - "--mnt-path" | ||
| - "$(FLUID_RUNTIME_MOUNT_PATH)" | ||
| - "--conf" | ||
| - "/etc/curvine/curvine.toml" | ||
| volumeMounts: | ||
| - name: shared-config-volume | ||
| mountPath: /etc/curvine # 配置文件存放目录 | ||
| imagePullPolicy: IfNotPresent | ||
| volumes: | ||
| # emptyDir 共享存储(init容器和主容器互通) | ||
| - name: shared-config-volume | ||
| emptyDir: { } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heads-up, out of scope for this PR but worth flagging in #6063: populating
Status.Selectorhere is correct and useful for any tooling that reads it directly, but HPA targeting CacheRuntime/scale will still not work until thescalesubresource is declared. Unlike AlluxioRuntime, JuiceFSRuntime, and VineyardRuntime, the CacheRuntime type currently only declares+kubebuilder:subresource:status— there is no+kubebuilder:subresource:scale:...selectorpath=.status.selectorannotation, and the generated CRD (config/crd/bases/data.fluid.io_cacheruntimes.yaml) reflects that (onlysubresources: status: {}). A follow-up PR adding the scale subresource will likely be needed to fully close the linked issue.