diff --git a/docs/en/samples/vineyard/configure_cache_size_of_vineyard_fuse.md b/docs/en/samples/vineyard/configure_cache_size_of_vineyard_fuse.md index 3a377d49bf2..038fd4acdc9 100644 --- a/docs/en/samples/vineyard/configure_cache_size_of_vineyard_fuse.md +++ b/docs/en/samples/vineyard/configure_cache_size_of_vineyard_fuse.md @@ -117,6 +117,95 @@ Type "help", "copyright", "credits" or "license" for more information. } ``` +### [Serverless Mode] Create an Application Pod and Mount the Vineyard Dataset + +Please note that the `serverless.fluid.io/inject` label shoule be set to `true` in the pod metadata when you want to run the application in serverless mode. + +```shell +$ cat <>> import vineyard +>>> client = vineyard.connect() +>>> client.status +{ + instance_id: 0, + deployment: local, + memory_usage: 0, + memory_limit: 21474836480, + deferred_requests: 0, + ipc_connections: 0, + rpc_connections: 1 +} +``` + ## Create Vineyard Runtime and set the fuse cache size to 1Gi Add the option `size: "1Gi"` to enable the cache of vineyard fuse and set the cache size to 1Gi. @@ -216,3 +305,172 @@ Type "help", "copyright", "credits" or "license" for more information. rpc_connections: 0 } ``` + +## [Serverless Mode] Create an Application Pod and Mount the Vineyard Dataset + +Add the label `serverless.fluid.io/inject: "true"` to make the pod run in serverless mode. + +```shell +$ cat <> /proc/1/fd/1 + preStop: + exec: + command: + - sh + - -c + - touch /tmp/prestop-marker && rm -f /runtime-mnt/vineyard/default/vineyard-with-fuse-cache/vineyard-fuse/vineyard-local.sock + /runtime-mnt/vineyard/default/vineyard-with-fuse-cache/vineyard-fuse/vineyard-worker.sock + && umount /runtime-mnt/vineyard/default/vineyard-with-fuse-cache/vineyard-fuse/rpc-conf + name: fluid-fuse-0 + resources: + requests: + memory: 1Gi + securityContext: + privileged: true + volumeMounts: + - mountPath: /runtime-mnt/vineyard/default/vineyard-with-fuse-cache + mountPropagation: Bidirectional + name: vineyard-fuse-mount-0 + - mountPath: /runtime-mnt/vineyard/default/vineyard-with-fuse-cache/vineyard-fuse/rpc-conf + name: vineyard-rpc-conf-0 + - mountPath: /check-mount.sh + name: check-mount-0 + readOnly: true + subPath: check-mount.sh + - command: + - /bin/bash + - -c + - | + pip3 install vineyard; + sleep infinity + image: python:3.10 + imagePullPolicy: IfNotPresent + name: demo + volumeMounts: + - mountPath: /var/run/vineyard + mountPropagation: HostToContainer + name: client-configuration + volumes: + - hostPath: + path: /runtime-mnt/vineyard/default/vineyard-with-fuse-cache/vineyard-fuse + type: "" + name: client-configuration + - hostPath: + path: /runtime-mnt/vineyard/default/vineyard-with-fuse-cache + type: DirectoryOrCreate + name: vineyard-fuse-mount-0 + - configMap: + defaultMode: 420 + name: vineyard-with-fuse-cache-rpc-conf + name: vineyard-rpc-conf-0 + - configMap: + defaultMode: 493 + name: vineyard-with-fuse-cache-vineyard-fuse-check-mount + name: check-mount-0 +``` + +Then we can check the client configurations in the pod, and notice there is +only one vineyard socket named `vineyard-local.sock` as it doesn't run on the same node as the vineyard worker. + +```shell +$ kubectl exec -it demo-app -c demo -- ls /var/r +un/vineyard +rpc-conf vineyard-config.yaml vineyard-local.sock + +# Check the vineyard client configurations +$ kubectl exec -it demo-app -c demo -- cat /var/ +run/vineyard/vineyard-config.yaml +Vineyard: + IPCSocket: vineyard-local.sock + RPCEndpoint: vineyard-with-fuse-cache-worker-0.vineyard-with-fuse-cache-worker.default:9600,vineyard-with-fuse-cache-worker-1.vineyard-with-fuse-cache-worker.default:9600 +``` + +Connect to the vineyard in the pod. + +```shell +$ kubectl exec -it demo-app -c demo -- python +Python 3.10.14 (main, Mar 25 2024, 21:45:25) [GCC 12.2.0] on linux +Type "help", "copyright", "credits" or "license" for more information. +>>> import vineyard +>>> client = vineyard.connect() +kubectl exec -it demo-app -c demo -- python +Python 3.10.14 (main, Mar 25 2024, 21:45:25) [GCC 12.2.0] on linux +Type "help", "copyright", "credits" or "license" for more information. +>>> import vineyard +>>> client = vineyard.connect() +>>> client.status +{ + instance_id: 3, + deployment: local, + memory_usage: 0, + memory_limit: 1073741824, + deferred_requests: 0, + ipc_connections: 1, + rpc_connections: 0 +} +``` diff --git a/docs/en/samples/vineyard/vineyard_runtime_in_serverless_mode.md b/docs/en/samples/vineyard/vineyard_runtime_in_serverless_mode.md new file mode 100644 index 00000000000..3335134a490 --- /dev/null +++ b/docs/en/samples/vineyard/vineyard_runtime_in_serverless_mode.md @@ -0,0 +1,143 @@ +# How to Use Vineyard Runtime in serverless mode + +## Install Fluid + +Refer to the [Installation Documentation](../../userguide/install.md) to complete the installation. + +## Create Vineyard Runtime and Dataset + +```shell +$ cat <>> import vineyard +>>> client = vineyard.connect() +>>> client.status +{ + instance_id: 0, + deployment: local, + memory_usage: 0, + memory_limit: 21474836480, + deferred_requests: 0, + ipc_connections: 0, + rpc_connections: 1 +} +``` diff --git a/pkg/application/inject/fuse/injector.go b/pkg/application/inject/fuse/injector.go index 95ff6e1b661..878cb664183 100644 --- a/pkg/application/inject/fuse/injector.go +++ b/pkg/application/inject/fuse/injector.go @@ -19,10 +19,12 @@ package fuse import ( "fmt" "reflect" + "strings" "github.com/fluid-cloudnative/fluid/pkg/application/inject/fuse/mutator" "github.com/fluid-cloudnative/fluid/pkg/common" "github.com/fluid-cloudnative/fluid/pkg/ddc/base" + "github.com/fluid-cloudnative/fluid/pkg/ddc/vineyard" "github.com/fluid-cloudnative/fluid/pkg/utils" "github.com/fluid-cloudnative/fluid/pkg/utils/applications/defaultapp" @@ -162,9 +164,13 @@ func (s *Injector) inject(in runtime.Object, runtimeInfos map[string]base.Runtim continue } - platform := s.getServerlessPlatformFromMeta(podSpecs.MetaObj) + // check whether the podSpecs is using vineyard runtime + platform := s.checkVineyardRuntime(podSpecs, runtimeInfos) if len(platform) == 0 { - return out, fmt.Errorf("can't find any supported platform-specific mutator in pod's metadata") + platform = s.getServerlessPlatformFromMeta(podSpecs.MetaObj) + if len(platform) == 0 { + return out, fmt.Errorf("can't find any supported platform-specific mutator in pod's metadata") + } } mutatorBuildOpts := mutator.MutatorBuildOpts{ @@ -289,3 +295,36 @@ func (s *Injector) labelInjectionDone(pod common.FluidObject) error { func (s *Injector) getServerlessPlatformFromMeta(metaObj metav1.ObjectMeta) string { return utils.GetServerlessPlatfrom(metaObj.Labels) } + +// check whether the podSpecs is using vineyard runtime +func (s *Injector) checkVineyardRuntime(podSpecs *mutator.MutatingPodSpecs, runtimeInfos map[string]base.RuntimeInfoInterface) string { + pvcNames := make(map[string]bool) + for _, volume := range podSpecs.Volumes { + if volume.PersistentVolumeClaim == nil { + continue + } + pvcNames[volume.PersistentVolumeClaim.ClaimName] = true + } + for pvcName, runtimeInfo := range runtimeInfos { + if _, ok := pvcNames[pvcName]; ok && runtimeInfo.GetRuntimeType() == common.VineyardRuntime { + template, err := runtimeInfo.GetFuseContainerTemplate() + if err != nil { + s.log.Error(err, "failed to get fuse container template for runtime \"%s/%s\"", runtimeInfo.GetNamespace(), runtimeInfo.GetName()) + return "" + } + if len(template.FuseContainer.Env) == 0 { + return "" + } + for _, env := range template.FuseContainer.Env { + if env.Name == strings.ToUpper(vineyard.VineyarddSize) { + if env.Value == "0" { + return utils.VineyardRuntime + } else { + return "" + } + } + } + } + } + return "" +} diff --git a/pkg/application/inject/fuse/injector_test.go b/pkg/application/inject/fuse/injector_test.go index 8b9aff49f18..c46aed8feb8 100644 --- a/pkg/application/inject/fuse/injector_test.go +++ b/pkg/application/inject/fuse/injector_test.go @@ -1138,6 +1138,441 @@ func TestInjectPod(t *testing.T) { }, wantErr: nil, }, + { + name: "inject_pod_with_vineyard_runtime_when_cache_size_is_zero", + dataset: &datav1alpha1.Dataset{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vineyard", + Namespace: "default", + }, + }, + in: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app", + Namespace: "default", + Labels: map[string]string{ + common.InjectServerless: common.True, + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "python:3.10", + Name: "app", + VolumeMounts: []corev1.VolumeMount{ + { + Name: "vineyard-volume", + MountPath: "/vineyard", + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "vineyard-volume", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "vineyard", + }, + }, + }, + }, + }, + }, + pv: &corev1.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vineyard", + }, + Spec: corev1.PersistentVolumeSpec{ + PersistentVolumeSource: corev1.PersistentVolumeSource{ + CSI: &corev1.CSIPersistentVolumeSource{ + Driver: "fuse.csi.fluid.io", + VolumeAttributes: map[string]string{ + common.VolumeAttrMountType: common.VineyardRuntime, + }, + }, + }, + }, + }, + pvc: &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vineyard", + Namespace: "default", + }, Spec: corev1.PersistentVolumeClaimSpec{ + VolumeName: "vineyard", + }, + }, + infos: map[string]runtimeInfo{ + "vineyard": { + name: "vineyard", + namespace: "default", + runtimeType: common.VineyardRuntime, + }, + }, + fuse: &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vineyard-fuse", + Namespace: "default", + }, + Spec: appsv1.DaemonSetSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "fuse", + Image: "test", + Env: []corev1.EnvVar{ + { + Name: "SIZE", + Value: "0", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "vineyard-fuse-mount", + MountPath: "/runtime-mnt/vineyard/default/vineyard", + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "vineyard-fuse-mount", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/runtime-mnt/vineyard/default/vineyard", + Type: &hostPathDirectoryOrCreate, + }, + }, + }, + }, + }, + }, + }, + }, + want: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app", + Namespace: "default", + Labels: map[string]string{ + common.InjectServerless: common.True, + common.InjectSidecarDone: common.True, + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "python:3.10", + Name: "app", + Env: []corev1.EnvVar{ + { + Name: "VINEYARD_RPC_ENDPOINT", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "vineyard-rpc-conf", + }, + Key: "VINEYARD_RPC_ENDPOINT", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{}, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "vineyard-rpc-conf", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "vineyard-rpc-conf", + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "inject_pod_with_vineyard_runtime_when_cache_size_is_not_zero", + dataset: &datav1alpha1.Dataset{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vineyard1", + Namespace: "default", + }, + }, + in: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "default", + Labels: map[string]string{ + common.InjectServerless: common.True, + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "python:3.10", + Name: "app", + VolumeMounts: []corev1.VolumeMount{ + { + Name: "vineyard-volume", + MountPath: "/vineyard", + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "vineyard-volume", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "vineyard", + }, + }, + }, + }, + }, + }, + pv: &corev1.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vineyard1", + }, + Spec: corev1.PersistentVolumeSpec{ + PersistentVolumeSource: corev1.PersistentVolumeSource{ + CSI: &corev1.CSIPersistentVolumeSource{ + Driver: "fuse.csi.fluid.io", + VolumeAttributes: map[string]string{ + common.VolumeAttrMountType: common.VineyardRuntime, + common.VolumeAttrFluidPath: "/runtime-mnt/vineyard/default/vineyard/vineyard-fuse", + }, + }, + }, + }, + }, + pvc: &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vineyard1", + Namespace: "default", + }, Spec: corev1.PersistentVolumeClaimSpec{ + VolumeName: "vineyard1", + }, + }, + infos: map[string]runtimeInfo{ + "vineyard": { + name: "vineyard1", + namespace: "default", + runtimeType: common.VineyardRuntime, + }, + }, + fuse: &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vineyard1-fuse", + Namespace: "default", + }, + Spec: appsv1.DaemonSetSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "fuse", + Image: "test", + Env: []corev1.EnvVar{ + { + Name: "MOUNT_DIR", + Value: "/runtime-mnt/vineyard/default/vineyard", + }, + { + Name: "FUSE_DIR", + Value: "/runtime-mnt/vineyard/default/vineyard/vineyard-fuse", + }, + { + Name: "RPC_CONF_DIR", + Value: "/runtime-mnt/vineyard/default/vineyard/vineyard-fuse/rpc-conf", + }, + { + Name: "PRESTOP_MARKER", + Value: "/tmp/prestop-marker", + }, + { + Name: "SIZE", + Value: "10Gi", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "vineyard-fuse-mount", + MountPath: "/runtime-mnt/vineyard/default/vineyard", + }, + { + Name: "vineyard-rpc-conf", + MountPath: "/runtime-mnt/vineyard/default/vineyard/vineyard-fuse/rpc-conf", + }, + }, + Lifecycle: &corev1.Lifecycle{ + PreStop: &corev1.LifecycleHandler{ + Exec: &corev1.ExecAction{ + Command: []string{ + "sh", + "-c", + "touch /tmp/prestop-marker && { rm /runtime-mnt/vineyard/default/vineyard/vineyard-fuse/vineyard-worker.sock || true; } && umount /runtime-mnt/vineyard/default/vineyard/vineyard-fuse/rpc-conf", + }, + }, + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "vineyard-fuse-mount", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/runtime-mnt/vineyard/default/vineyard", + Type: &hostPathDirectoryOrCreate, + }, + }, + }, + { + Name: "vineyard-rpc-conf", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "vineyard-rpc-conf", + }, + }, + }, + }, + }, + }, + }, + }, + }, + want: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "default", + Labels: map[string]string{ + common.InjectServerless: common.True, + common.InjectSidecarDone: common.True, + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: common.FuseContainerName + "-0", + Image: "test", + Env: []corev1.EnvVar{ + { + Name: "MOUNT_DIR", + Value: "/runtime-mnt/vineyard/default/vineyard", + }, + { + Name: "FUSE_DIR", + Value: "/runtime-mnt/vineyard/default/vineyard/vineyard-fuse", + }, + { + Name: "RPC_CONF_DIR", + Value: "/runtime-mnt/vineyard/default/vineyard/vineyard-fuse/rpc-conf", + }, + { + Name: "PRESTOP_MARKER", + Value: "/tmp/prestop-marker", + }, + { + Name: "SIZE", + Value: "10Gi", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "vineyard-fuse-mount-0", + MountPath: "/runtime-mnt/vineyard/default/vineyard", + }, { + Name: "vineyard-rpc-conf-0", + MountPath: "/runtime-mnt/vineyard/default/vineyard/vineyard-fuse/rpc-conf", + }, { + Name: "check-mount-0", + ReadOnly: true, + MountPath: "/check-mount.sh", + SubPath: "check-mount.sh", + }, + }, Lifecycle: &corev1.Lifecycle{ + PreStop: &corev1.LifecycleHandler{ + Exec: &corev1.ExecAction{ + Command: []string{ + "sh", + "-c", + "touch /tmp/prestop-marker && { rm /runtime-mnt/vineyard/default/vineyard/vineyard-fuse/vineyard-worker.sock || true; } && umount /runtime-mnt/vineyard/default/vineyard/vineyard-fuse/rpc-conf", + }, + }, + }, + PostStart: &corev1.LifecycleHandler{ + Exec: &corev1.ExecAction{ + Command: []string{ + "bash", + "-c", + "time /check-mount.sh /runtime-mnt/vineyard/default/vineyard vineyard >> /proc/1/fd/1", + }, + }, + }, + }, + }, { + Name: "app", + Image: "python:3.10", + VolumeMounts: []corev1.VolumeMount{ + { + Name: "vineyard-volume", + MountPath: "/vineyard", + MountPropagation: &mountPropagationHostToContainer, + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "vineyard-volume", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/runtime-mnt/vineyard/default/vineyard/vineyard-fuse", + }, + }, + }, + { + Name: "vineyard-fuse-mount-0", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/runtime-mnt/vineyard/default/vineyard", + Type: &hostPathDirectoryOrCreate, + }, + }, + }, + { + Name: "vineyard-rpc-conf-0", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "vineyard-rpc-conf", + }, + }, + }, + }, + { + Name: "check-mount-0", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "vineyard1-vineyard-check-mount", + }, + DefaultMode: utilpointer.Int32(mode), + }, + }, + }, + }, + }, + }, + }, } objs := []runtime.Object{} @@ -1149,6 +1584,17 @@ func TestInjectPod(t *testing.T) { objs = append(objs, testcase.fuse, testcase.pv, testcase.pvc, testcase.dataset) } + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vineyard-rpc-conf", + Namespace: "default", + }, + Data: map[string]string{ + "VINEYARD_RPC_ENDPOINT": "127.0.0.1", + }, + } + objs = append(objs, cm) + fakeClient := fake.NewFakeClientWithScheme(s, objs...) for _, testcase := range testcases { @@ -1222,7 +1668,7 @@ func TestInjectPod(t *testing.T) { t.Errorf("testcase %s failed, due to %v", testcase.name, err) } - t.Errorf("testcase %s failed, want %v, Got %v", testcase.name, string(want), string(outYaml)) + t.Errorf("testcase %s failed, want %v, Got %v", testcase.name, string(want), string(outYaml)) } } else { t.Errorf("testcase %s failed due to missing the container %s", testcase.name, k) @@ -1251,7 +1697,7 @@ func TestInjectPod(t *testing.T) { t.Errorf("testcase %s failed, due to %v", testcase.name, err) } - t.Errorf("testcase %s failed, want %v, Got %v", testcase.name, string(want), string(outYaml)) + t.Errorf("testcase %s failed, want %v, Got %v", testcase.name, string(want), string(outYaml)) } } else { t.Errorf("testcase %s failed due to missing the volume %s", testcase.name, k) diff --git a/pkg/application/inject/fuse/mount_point_script.go b/pkg/application/inject/fuse/mount_point_script.go index e0307c5849c..d79b4ab0b59 100644 --- a/pkg/application/inject/fuse/mount_point_script.go +++ b/pkg/application/inject/fuse/mount_point_script.go @@ -23,6 +23,7 @@ import ( "github.com/fluid-cloudnative/fluid/pkg/application/inject/fuse/mutator" "github.com/fluid-cloudnative/fluid/pkg/application/inject/fuse/poststart" + "github.com/fluid-cloudnative/fluid/pkg/common" "github.com/fluid-cloudnative/fluid/pkg/ddc/base" "github.com/fluid-cloudnative/fluid/pkg/utils" "github.com/fluid-cloudnative/fluid/pkg/utils/kubeclient" @@ -38,6 +39,15 @@ func (s *Injector) injectCheckMountReadyScript(podSpecs *mutator.MutatingPodSpec return nil } + // Skip if there is only vineyard runtime + if len(runtimeInfos) == 1 { + for _, v := range runtimeInfos { + if v.GetRuntimeType() == common.VineyardRuntime { + return nil + } + } + } + // Choose the first runtime info's namespace for _, v := range runtimeInfos { namespace = v.GetNamespace() diff --git a/pkg/application/inject/fuse/mutator/mutator.go b/pkg/application/inject/fuse/mutator/mutator.go index c2d54e26c0d..d57f17ca4cc 100644 --- a/pkg/application/inject/fuse/mutator/mutator.go +++ b/pkg/application/inject/fuse/mutator/mutator.go @@ -45,6 +45,7 @@ type MutatorBuildOpts struct { var mutatorBuildFn map[string]func(MutatorBuildOpts) Mutator = map[string]func(MutatorBuildOpts) Mutator{ utils.PlatformDefault: NewDefaultMutator, utils.PlatformUnprivileged: NewUnprivilegedMutator, + utils.VineyardRuntime: NewVineyardMutator, } func BuildMutator(opts MutatorBuildOpts, platform string) (Mutator, error) { diff --git a/pkg/application/inject/fuse/mutator/mutator_vineyard.go b/pkg/application/inject/fuse/mutator/mutator_vineyard.go new file mode 100644 index 00000000000..15ab777075f --- /dev/null +++ b/pkg/application/inject/fuse/mutator/mutator_vineyard.go @@ -0,0 +1,139 @@ +/* +Copyright 2023 The Fluid Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mutator + +import ( + "context" + "fmt" + + "github.com/fluid-cloudnative/fluid/pkg/common" + "github.com/fluid-cloudnative/fluid/pkg/ddc/base" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" +) + +type VineyardMutator struct { + // VineyardMutator inherits from DefaultMutator + DefaultMutator +} + +var _ Mutator = &VineyardMutator{} + +func NewVineyardMutator(opts MutatorBuildOpts) Mutator { + return &VineyardMutator{ + DefaultMutator: DefaultMutator{ + options: opts.Options, + client: opts.Client, + log: opts.Log, + Specs: opts.Specs, + }, + } +} + +func (mutator *VineyardMutator) MutateWithRuntimeInfo(pvcName string, runtimeInfo base.RuntimeInfoInterface, nameSuffix string) error { + // check if the runtime is Vineyard + if runtimeInfo.GetRuntimeType() != common.VineyardRuntime { + return fmt.Errorf("runtime %s is not %s", runtimeInfo.GetName(), common.VineyardRuntime) + } + + // check whether the vineyard rpc configmap is existed + cm := &corev1.ConfigMap{} + cmName := pvcName + common.VineyardConfigmapSuffix + cmNamespace := runtimeInfo.GetNamespace() + if err := mutator.client.Get(context.TODO(), types.NamespacedName{Name: cmName, Namespace: cmNamespace}, cm); err != nil { + return err + } + + // add vineyard configmap volume + volumes := mutator.Specs.Volumes + overriddenVolumes := make([]corev1.Volume, 0) + pvcVolumeName := "" + for _, volume := range volumes { + switch { + case volume.Name == common.VineyardConfigmapVolumeName: + continue + case volume.PersistentVolumeClaim != nil && volume.PersistentVolumeClaim.ClaimName == pvcName: + pvcVolumeName = volume.Name + continue + } + overriddenVolumes = append(overriddenVolumes, volume) + } + + overriddenVolumes = append(overriddenVolumes, corev1.Volume{ + Name: common.VineyardConfigmapVolumeName, + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: cmName, + }, + }, + }, + }) + + mutator.Specs.Volumes = overriddenVolumes + + // inject vineyard volume as environment variable + containers := mutator.Specs.Containers + for i := range containers { + overriddenEnvs := make([]corev1.EnvVar, 0) + for j := range containers[i].Env { + if containers[i].Env[j].Name != common.VineyardConfigmapVolumeName { + overriddenEnvs = append(overriddenEnvs, containers[i].Env[j]) + } + } + overriddenEnvs = append(overriddenEnvs, corev1.EnvVar{ + Name: common.VineyardRPCEndpoint, + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: cmName, + }, + Key: common.VineyardRPCEndpoint, + }, + }, + }) + overridenVolumeMounts := make([]corev1.VolumeMount, 0) + for _, vm := range containers[i].VolumeMounts { + if pvcVolumeName != "" && vm.Name != pvcVolumeName { + overridenVolumeMounts = append(overridenVolumeMounts, vm) + } + } + + containers[i].Env = overriddenEnvs + containers[i].VolumeMounts = overridenVolumeMounts + } + mutator.Specs.Containers = containers + + volumesMounts := mutator.Specs.VolumeMounts + overridenVolumeMounts := make([]corev1.VolumeMount, 0) + for _, volumeMount := range volumesMounts { + if pvcVolumeName != "" && volumeMount.Name != pvcVolumeName { + overridenVolumeMounts = append(overridenVolumeMounts, volumeMount) + } + } + mutator.Specs.VolumeMounts = overridenVolumeMounts + return nil +} + +func (mutator *VineyardMutator) PostMutate() error { + return mutator.DefaultMutator.PostMutate() +} + +func (mutator *VineyardMutator) GetMutatedPodSpecs() *MutatingPodSpecs { + return mutator.DefaultMutator.GetMutatedPodSpecs() +} diff --git a/pkg/common/vineyard.go b/pkg/common/vineyard.go index 4965d5a6bd3..109f8d18651 100644 --- a/pkg/common/vineyard.go +++ b/pkg/common/vineyard.go @@ -36,6 +36,12 @@ const ( DefultVineyardFuseImage = "registry.aliyuncs.com/vineyard/vineyard-fluid-fuse:v0.22.1" VineyardEngineImpl = VineyardRuntime + + VineyardConfigmapSuffix = "-rpc-conf" + + VineyardConfigmapVolumeName = "vineyard-rpc-conf" + + VineyardRPCEndpoint = "VINEYARD_RPC_ENDPOINT" ) var ( diff --git a/pkg/utils/annotations.go b/pkg/utils/annotations.go index d61d55d9db2..4daa9c5291d 100644 --- a/pkg/utils/annotations.go +++ b/pkg/utils/annotations.go @@ -69,10 +69,11 @@ func AppContainerPostStartInjectEnabled(infos map[string]string) (match bool) { return enabled(infos, common.InjectAppPostStart) } -// ---- Utils functions to decide serverless platform ---- +// ---- Utils functions to decide serverless platform or specific runtime ---- const ( PlatformDefault = "Default" PlatformUnprivileged = "Unprivileged" + VineyardRuntime = "Vineyard" ) func GetServerlessPlatfrom(infos map[string]string) (platform string) { diff --git a/pkg/utils/kubeclient/volume_mount.go b/pkg/utils/kubeclient/volume_mount.go index a0d10cbb517..76499d9c1c0 100644 --- a/pkg/utils/kubeclient/volume_mount.go +++ b/pkg/utils/kubeclient/volume_mount.go @@ -74,13 +74,15 @@ func pvcNamesFromVolumes(knownVolumeNames []string, volumes []corev1.Volume) (pv func GetFuseMountInContainer(mountType string, container corev1.Container) (volumeMount corev1.VolumeMount, err error) { kv := map[string]string{ - common.JindoMountType: common.JindoChartName, - common.JindoRuntime: common.JindoChartName, - common.AlluxioMountType: common.AlluxioChart, - common.AlluxioRuntime: common.AlluxioChart, - common.GooseFSMountType: common.GooseFSChart, - common.JuiceFSMountType: common.JuiceFSChart, - common.JuiceFSRuntime: common.JuiceFSChart, + common.JindoMountType: common.JindoChartName, + common.JindoRuntime: common.JindoChartName, + common.AlluxioMountType: common.AlluxioChart, + common.AlluxioRuntime: common.AlluxioChart, + common.GooseFSMountType: common.GooseFSChart, + common.JuiceFSMountType: common.JuiceFSChart, + common.JuiceFSRuntime: common.JuiceFSChart, + common.VineyardRuntime: common.VineyardChart, + common.VineyardMountType: common.VineyardChart, } volumeMountName := "" @@ -116,6 +118,7 @@ func GetMountPathInContainer(container corev1.Container) (string, error) { common.AlluxioChart: "alluxio-fuse", common.GooseFSChart: "goosefs-fuse", common.JuiceFSChart: "juicefs-fuse", + common.VineyardChart: "vineyard-fuse", } // consider the env FLUID_FUSE_MOUNTPOINT if len(container.Env) > 0 {