diff --git a/apis/apps/v1alpha1/sidecarset_types.go b/apis/apps/v1alpha1/sidecarset_types.go index 601060add..2fa26ad4b 100644 --- a/apis/apps/v1alpha1/sidecarset_types.go +++ b/apis/apps/v1alpha1/sidecarset_types.go @@ -132,9 +132,14 @@ type SidecarContainer struct { UpgradeStrategy SidecarContainerUpgradeStrategy `json:"upgradeStrategy,omitempty"` // If ShareVolumePolicy is enabled, the sidecar container will share the other container's VolumeMounts - // in the pod(don't contains the injected sidecar container). + // in the pod(not including the injected sidecar container). ShareVolumePolicy ShareVolumePolicy `json:"shareVolumePolicy,omitempty"` + // If ShareVolumeDevicePolicy is enabled, the sidecar container will share the other container's VolumeDevices + // in the pod(don't contain the injected sidecar container). + // This is a pointer to ensure that the sidecarset-hash does not change if the user does not configure this field, mainly for compatibility with older versions. + ShareVolumeDevicePolicy *ShareVolumePolicy `json:"shareVolumeDevicePolicy,omitempty"` + // TransferEnv will transfer env info from other container // SourceContainerName is pod.spec.container[x].name; EnvName is pod.spec.container[x].Env.name TransferEnv []TransferEnvVar `json:"transferEnv,omitempty"` diff --git a/apis/apps/v1alpha1/zz_generated.deepcopy.go b/apis/apps/v1alpha1/zz_generated.deepcopy.go index 7eb70819c..53ef3f181 100644 --- a/apis/apps/v1alpha1/zz_generated.deepcopy.go +++ b/apis/apps/v1alpha1/zz_generated.deepcopy.go @@ -2711,6 +2711,11 @@ func (in *SidecarContainer) DeepCopyInto(out *SidecarContainer) { in.Container.DeepCopyInto(&out.Container) out.UpgradeStrategy = in.UpgradeStrategy out.ShareVolumePolicy = in.ShareVolumePolicy + if in.ShareVolumeDevicePolicy != nil { + in, out := &in.ShareVolumeDevicePolicy, &out.ShareVolumeDevicePolicy + *out = new(ShareVolumePolicy) + **out = **in + } if in.TransferEnv != nil { in, out := &in.TransferEnv, &out.TransferEnv *out = make([]TransferEnvVar, len(*in)) diff --git a/config/crd/bases/apps.kruise.io_sidecarsets.yaml b/config/crd/bases/apps.kruise.io_sidecarsets.yaml index 06fada405..fb5b2c97a 100644 --- a/config/crd/bases/apps.kruise.io_sidecarsets.yaml +++ b/config/crd/bases/apps.kruise.io_sidecarsets.yaml @@ -73,10 +73,19 @@ spec: otherwise it will be injected into the back. default BeforeAppContainerType type: string + shareVolumeDevicePolicy: + description: |- + If ShareVolumeDevicePolicy is enabled, the sidecar container will share the other container's VolumeDevices + in the pod(don't contain the injected sidecar container). + This is a pointer to ensure that the sidecarset-hash does not change if the user does not configure this field, mainly for compatibility with older versions. + properties: + type: + type: string + type: object shareVolumePolicy: description: |- If ShareVolumePolicy is enabled, the sidecar container will share the other container's VolumeMounts - in the pod(don't contains the injected sidecar container). + in the pod(not including the injected sidecar container). properties: type: type: string @@ -173,10 +182,19 @@ spec: otherwise it will be injected into the back. default BeforeAppContainerType type: string + shareVolumeDevicePolicy: + description: |- + If ShareVolumeDevicePolicy is enabled, the sidecar container will share the other container's VolumeDevices + in the pod(don't contain the injected sidecar container). + This is a pointer to ensure that the sidecarset-hash does not change if the user does not configure this field, mainly for compatibility with older versions. + properties: + type: + type: string + type: object shareVolumePolicy: description: |- If ShareVolumePolicy is enabled, the sidecar container will share the other container's VolumeMounts - in the pod(don't contains the injected sidecar container). + in the pod(not including the injected sidecar container). properties: type: type: string diff --git a/pkg/control/sidecarcontrol/util.go b/pkg/control/sidecarcontrol/util.go index 28c470bba..517df0c9a 100644 --- a/pkg/control/sidecarcontrol/util.go +++ b/pkg/control/sidecarcontrol/util.go @@ -313,6 +313,11 @@ func IsSharePodVolumeMounts(container *appsv1alpha1.SidecarContainer) bool { return container.ShareVolumePolicy.Type == appsv1alpha1.ShareVolumePolicyEnabled } +// TODO: +// If you share volume, the volume path of the business container may conflict with the volume path of the sidecar container, +// resulting in a failed pod creation. +// For example, if the user's main container volumeDevice has devicePath /var/log and the sidecar container has volumeMounts path /var/log, +// the path will conflict and the creation will fail. func GetInjectedVolumeMountsAndEnvs(control SidecarControl, sidecarContainer *appsv1alpha1.SidecarContainer, pod *corev1.Pod) ([]corev1.VolumeMount, []corev1.EnvVar) { if !IsSharePodVolumeMounts(sidecarContainer) { return nil, nil @@ -350,9 +355,38 @@ func GetInjectedVolumeMountsAndEnvs(control SidecarControl, sidecarContainer *ap } } } + // TODO: share pod.spec.initContainers[*].volumeMounts return injectedMounts, injectedEnvs } +func IsSharePodVolumeDevices(container *appsv1alpha1.SidecarContainer) bool { + if container.ShareVolumeDevicePolicy == nil { + return false + } + return container.ShareVolumeDevicePolicy.Type == appsv1alpha1.ShareVolumePolicyEnabled +} + +func GetInjectedVolumeDevices(sidecarContainer *appsv1alpha1.SidecarContainer, pod *corev1.Pod) []corev1.VolumeDevice { + if !IsSharePodVolumeDevices(sidecarContainer) { + return nil + } + + // injected volumeDevices + var volumeDevices []corev1.VolumeDevice + for _, appContainer := range pod.Spec.Containers { + // ignore the injected sidecar container + if IsInjectedSidecarContainerInPod(&appContainer) { + continue + } + + for _, volumeDevice := range appContainer.VolumeDevices { + volumeDevices = append(volumeDevices, volumeDevice) + } + } + // TODO: share pod.spec.initContainers[*].volumeDevices + return volumeDevices +} + func GetSidecarTransferEnvs(sidecarContainer *appsv1alpha1.SidecarContainer, pod *corev1.Pod) (injectedEnvs []corev1.EnvVar) { // pre-process envs in pod, format: container.name/env.name -> container.env // if SourceContainerName is set, use it as source container name diff --git a/pkg/control/sidecarcontrol/util_test.go b/pkg/control/sidecarcontrol/util_test.go index 152cb780c..da1baae39 100644 --- a/pkg/control/sidecarcontrol/util_test.go +++ b/pkg/control/sidecarcontrol/util_test.go @@ -1265,3 +1265,130 @@ func TestPodMatchedSidecarSet(t *testing.T) { }) } } + +func TestGetInjectedVolumeDevices(t *testing.T) { + cases := []struct { + name string + getSidecarContainer func() *appsv1alpha1.SidecarContainer + getPod func() *corev1.Pod + expect []corev1.VolumeDevice + }{ + { + name: "ShareVolumeDevicePolicy, disable", + getSidecarContainer: func() *appsv1alpha1.SidecarContainer { + obj := &appsv1alpha1.SidecarContainer{} + return obj + }, + getPod: func() *corev1.Pod { + obj := &corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + VolumeDevices: []corev1.VolumeDevice{ + { + Name: "vd-1", + DevicePath: "/data/volume-1", + }, + }, + }, + { + VolumeDevices: []corev1.VolumeDevice{ + { + Name: "vd-2", + DevicePath: "/data/volume-2", + }, + }, + }, + { + VolumeDevices: []corev1.VolumeDevice{ + { + Name: "vd-3", + DevicePath: "/data/volume-3", + }, + }, + Env: []corev1.EnvVar{ + { + Name: SidecarEnvKey, + Value: "true", + }, + }, + }, + }, + }, + } + + return obj + }, + }, + { + name: "ShareVolumeDevicePolicy, disable", + getSidecarContainer: func() *appsv1alpha1.SidecarContainer { + obj := &appsv1alpha1.SidecarContainer{ + ShareVolumeDevicePolicy: &appsv1alpha1.ShareVolumePolicy{ + Type: appsv1alpha1.ShareVolumePolicyEnabled, + }, + } + return obj + }, + getPod: func() *corev1.Pod { + obj := &corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + VolumeDevices: []corev1.VolumeDevice{ + { + Name: "vd-1", + DevicePath: "/data/volume-1", + }, + }, + }, + { + VolumeDevices: []corev1.VolumeDevice{ + { + Name: "vd-2", + DevicePath: "/data/volume-2", + }, + }, + }, + { + VolumeDevices: []corev1.VolumeDevice{ + { + Name: "vd-3", + DevicePath: "/data/volume-3", + }, + }, + Env: []corev1.EnvVar{ + { + Name: SidecarEnvKey, + Value: "true", + }, + }, + }, + }, + }, + } + + return obj + }, + expect: []corev1.VolumeDevice{ + { + Name: "vd-1", + DevicePath: "/data/volume-1", + }, + { + Name: "vd-2", + DevicePath: "/data/volume-2", + }, + }, + }, + } + + for _, cs := range cases { + t.Run(cs.name, func(t *testing.T) { + vd := GetInjectedVolumeDevices(cs.getSidecarContainer(), cs.getPod()) + if !reflect.DeepEqual(vd, cs.expect) { + t.Fatalf("expect(%v), but get(%v)", cs.expect, vd) + } + }) + } +} diff --git a/pkg/controller/sidecarset/sidecarset_processor.go b/pkg/controller/sidecarset/sidecarset_processor.go index 09584187c..fc5a4f7f4 100644 --- a/pkg/controller/sidecarset/sidecarset_processor.go +++ b/pkg/controller/sidecarset/sidecarset_processor.go @@ -105,7 +105,18 @@ func (p *Processor) UpdateSidecarSet(sidecarSet *appsv1alpha1.SidecarSet) (recon return reconcile.Result{RequeueAfter: time.Second}, nil } - // 3. If sidecar container hot upgrade complete, then set the other one(empty sidecar container) image to HotUpgradeEmptyImage + // 3. SidecarSet upgrade strategy type is NotUpdate + if isSidecarSetNotUpdate(sidecarSet) { + return reconcile.Result{}, nil + } + + // 4. Paused indicates that the SidecarSet is paused to update matched pods + if sidecarSet.Spec.UpdateStrategy.Paused { + klog.V(3).InfoS("SidecarSet was paused", "sidecarSet", klog.KObj(sidecarSet)) + return reconcile.Result{}, nil + } + + // 5. If sidecar container hot upgrade complete, then set the other one(empty sidecar container) image to HotUpgradeEmptyImage if isSidecarSetHasHotUpgradeContainer(sidecarSet) { var podsInHotUpgrading []*corev1.Pod for _, pod := range pods { @@ -132,23 +143,12 @@ func (p *Processor) UpdateSidecarSet(sidecarSet *appsv1alpha1.SidecarSet) (recon } } - // 4. SidecarSet upgrade strategy type is NotUpdate - if isSidecarSetNotUpdate(sidecarSet) { - return reconcile.Result{}, nil - } - - // 5. sidecarset already updates all matched pods, then return + // 6. sidecarset already updates all matched pods, then return if isSidecarSetUpdateFinish(status) { klog.V(3).InfoS("SidecarSet matched pods were latest, and don't need update", "sidecarSet", klog.KObj(sidecarSet), "matchedPodCount", len(pods)) return reconcile.Result{}, nil } - // 6. Paused indicates that the SidecarSet is paused to update matched pods - if sidecarSet.Spec.UpdateStrategy.Paused { - klog.V(3).InfoS("SidecarSet was paused", "sidecarSet", klog.KObj(sidecarSet)) - return reconcile.Result{}, nil - } - // 7. upgrade pod sidecar if err := p.updatePods(control, pods); err != nil { return reconcile.Result{}, err @@ -572,7 +572,7 @@ func updatePodSidecarContainer(control sidecarcontrol.SidecarControl, pod *corev // when volumeMounts SubPathExpr contains expansions, then need copy container EnvVars(injectEnvs) injectedMounts, injectedEnvs := sidecarcontrol.GetInjectedVolumeMountsAndEnvs(control, &sidecarContainer, pod) // merge VolumeMounts from sidecar.VolumeMounts and shared VolumeMounts - sidecarContainer.VolumeMounts = util.MergeVolumeMounts(sidecarContainer.VolumeMounts, injectedMounts) + sidecarContainer.VolumeMounts = util.MergeVolumeMounts(sidecarContainer.Container, injectedMounts) // get injected env & mounts explicitly so that can be compared with old ones in pod transferEnvs := sidecarcontrol.GetSidecarTransferEnvs(&sidecarContainer, pod) @@ -581,6 +581,10 @@ func updatePodSidecarContainer(control sidecarcontrol.SidecarControl, pod *corev // merged Env from sidecar.Env and transfer envs sidecarContainer.Env = util.MergeEnvVar(sidecarContainer.Env, transferEnvs) + // merge volumeDevice + injectedDevices := sidecarcontrol.GetInjectedVolumeDevices(&sidecarContainer, pod) + sidecarContainer.VolumeDevices = util.MergeVolumeDevices(sidecarContainer.Container, injectedDevices) + // upgrade sidecar container to latest newContainer := control.UpgradeSidecarContainer(&sidecarContainer, pod) // no change, then continue diff --git a/pkg/util/pods.go b/pkg/util/pods.go index 8e4fa8340..5957f2e34 100644 --- a/pkg/util/pods.go +++ b/pkg/util/pods.go @@ -76,18 +76,44 @@ func DiffPods(pods1, pods2 []*v1.Pod) (ret []*v1.Pod) { return } -func MergeVolumeMounts(original, additional []v1.VolumeMount) []v1.VolumeMount { - mountpoints := sets.NewString() - for _, mount := range original { - mountpoints.Insert(mount.MountPath) +func MergeVolumeMounts(container v1.Container, additional []v1.VolumeMount) []v1.VolumeMount { + mountPoints := sets.NewString() + var original []v1.VolumeMount + for _, mount := range container.VolumeMounts { + mountPoints.Insert(mount.MountPath) + original = append(original, mount) + } + for _, mount := range container.VolumeDevices { + mountPoints.Insert(mount.DevicePath) } for _, mount := range additional { - if mountpoints.Has(mount.MountPath) { + if mountPoints.Has(mount.MountPath) { continue } original = append(original, mount) - mountpoints.Insert(mount.MountPath) + mountPoints.Insert(mount.MountPath) + } + return original +} + +func MergeVolumeDevices(container v1.Container, additional []v1.VolumeDevice) []v1.VolumeDevice { + mountPoints := sets.NewString() + var original []v1.VolumeDevice + for _, mount := range container.VolumeDevices { + mountPoints.Insert(mount.DevicePath) + original = append(original, mount) + } + for _, mount := range container.VolumeMounts { + mountPoints.Insert(mount.MountPath) + } + + for _, mount := range additional { + if mountPoints.Has(mount.DevicePath) { + continue + } + original = append(original, mount) + mountPoints.Insert(mount.DevicePath) } return original } diff --git a/pkg/util/pods_test.go b/pkg/util/pods_test.go index 3dfd3fdb5..1dcc7d358 100644 --- a/pkg/util/pods_test.go +++ b/pkg/util/pods_test.go @@ -21,36 +21,138 @@ import ( "reflect" "testing" - "k8s.io/apimachinery/pkg/util/sets" - v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/util/sets" ) func TestMergeVolumeMounts(t *testing.T) { - original := []v1.VolumeMount{ + tests := []struct { + name string + getContainer func() v1.Container + getVolumeMounts func() []v1.VolumeMount + expect []v1.VolumeMount + }{ { - MountPath: "/origin-1", - }, - { - MountPath: "/share", + name: "test1", + getContainer: func() v1.Container { + obj := v1.Container{ + VolumeMounts: []v1.VolumeMount{ + { + MountPath: "/origin-1", + }, + { + Name: "origin-share", + MountPath: "/share", + }, + }, + VolumeDevices: []v1.VolumeDevice{ + { + DevicePath: "/device-1", + }, + }, + } + return obj + }, + getVolumeMounts: func() []v1.VolumeMount { + return []v1.VolumeMount{ + { + MountPath: "/addition-1", + }, + { + Name: "addition-share", + MountPath: "/share", + }, + { + MountPath: "/device-1", + }, + } + }, + expect: []v1.VolumeMount{ + { + MountPath: "/origin-1", + }, + { + Name: "origin-share", + MountPath: "/share", + }, + { + MountPath: "/addition-1", + }, + }, }, } - additional := []v1.VolumeMount{ + for i, test := range tests { + obj := MergeVolumeMounts(test.getContainer(), test.getVolumeMounts()) + if !reflect.DeepEqual(obj, test.expect) { + t.Fatalf("case %d: expect(%v), but get(%v)", i, test.expect, obj) + } + } +} + +func TestMergeVolumeDevices(t *testing.T) { + tests := []struct { + name string + getContainer func() v1.Container + getVolumeDevices func() []v1.VolumeDevice + expect []v1.VolumeDevice + }{ { - MountPath: "/addition-1", - }, - { - MountPath: "/share", + name: "inject test1", + getContainer: func() v1.Container { + obj := v1.Container{ + VolumeMounts: []v1.VolumeMount{ + { + MountPath: "/log", + }, + }, + VolumeDevices: []v1.VolumeDevice{ + { + DevicePath: "/origin-1", + }, + { + Name: "origin-pvc", + DevicePath: "/share", + }, + }, + } + return obj + }, + getVolumeDevices: func() []v1.VolumeDevice { + additional := []v1.VolumeDevice{ + { + DevicePath: "/addition-1", + }, + { + Name: "target-pvc", + DevicePath: "/share", + }, + { + DevicePath: "/log", + }, + } + return additional + }, + expect: []v1.VolumeDevice{ + { + DevicePath: "/origin-1", + }, + { + Name: "origin-pvc", + DevicePath: "/share", + }, + { + DevicePath: "/addition-1", + }, + }, }, } - volumeMounts := MergeVolumeMounts(original, additional) - excepts := []string{"/origin-1", "/share", "/addition-1"} - for i, except := range excepts { - if volumeMounts[i].MountPath != except { - t.Fatalf("except VolumeMount(%s), but get %s", except, volumeMounts[i].MountPath) + for i, test := range tests { + obj := MergeVolumeDevices(test.getContainer(), test.getVolumeDevices()) + if !reflect.DeepEqual(obj, test.expect) { + t.Fatalf("case %d: expect(%v), but get(%v)", i, test.expect, obj) } } } diff --git a/pkg/webhook/pod/mutating/sidecarset.go b/pkg/webhook/pod/mutating/sidecarset.go index c841f8fe0..254f871fe 100644 --- a/pkg/webhook/pod/mutating/sidecarset.go +++ b/pkg/webhook/pod/mutating/sidecarset.go @@ -435,20 +435,26 @@ func buildSidecars(isUpdated bool, pod *corev1.Pod, oldPod *corev1.Pod, matchedS transferEnvs := sidecarcontrol.GetSidecarTransferEnvs(initContainer, pod) // append volumeMounts SubPathExpr environments transferEnvs = util.MergeEnvVar(transferEnvs, injectedEnvs) - klog.InfoS("try to inject initContainer sidecar", - "containerName", initContainer.Name, "namespace", pod.Namespace, "podName", pod.Name, "envs", transferEnvs, "volumeMounts", injectedMounts) // insert volumes that initContainers used for _, mount := range initContainer.VolumeMounts { volumesInSidecars = append(volumesInSidecars, *volumesMap[mount.Name]) } + for _, mount := range initContainer.VolumeDevices { + volumesInSidecars = append(volumesInSidecars, *volumesMap[mount.Name]) + } // merge VolumeMounts from sidecar.VolumeMounts and shared VolumeMounts - initContainer.VolumeMounts = util.MergeVolumeMounts(initContainer.VolumeMounts, injectedMounts) + initContainer.VolumeMounts = util.MergeVolumeMounts(initContainer.Container, injectedMounts) // add "IS_INJECTED" env in initContainer's envs initContainer.Env = append(initContainer.Env, corev1.EnvVar{Name: sidecarcontrol.SidecarEnvKey, Value: "true"}) // merged Env from sidecar.Env and transfer envs initContainer.Env = util.MergeEnvVar(initContainer.Env, transferEnvs) isInjecting = true + // merge volumeDevice + injectedDevices := sidecarcontrol.GetInjectedVolumeDevices(initContainer, pod) + initContainer.VolumeDevices = util.MergeVolumeDevices(initContainer.Container, injectedDevices) + klog.InfoS("try to inject initContainer sidecar", + "containerName", initContainer.Name, "namespace", pod.Namespace, "podName", pod.Name, "envs", transferEnvs, "volumeMounts", injectedMounts, "volumeDevices", injectedDevices) // when sidecar container UpgradeStrategy is HotUpgrade if sidecarcontrol.IsSidecarContainer(initContainer.Container) && sidecarcontrol.IsHotUpgradeContainer(initContainer) { hotContainers, annotations := injectHotUpgradeContainers(hotUpgradeWorkInfo, initContainer) @@ -475,8 +481,6 @@ func buildSidecars(isUpdated bool, pod *corev1.Pod, oldPod *corev1.Pod, matchedS transferEnvs := sidecarcontrol.GetSidecarTransferEnvs(sidecarContainer, pod) // append volumeMounts SubPathExpr environments transferEnvs = util.MergeEnvVar(transferEnvs, injectedEnvs) - klog.InfoS("try to inject Container sidecar", - "containerName", sidecarContainer.Name, "namespace", pod.Namespace, "podName", pod.Name, "envs", transferEnvs, "volumeMounts", injectedMounts) //when update pod object if isUpdated { // judge whether inject sidecar container into pod @@ -499,13 +503,21 @@ func buildSidecars(isUpdated bool, pod *corev1.Pod, oldPod *corev1.Pod, matchedS for _, mount := range sidecarContainer.VolumeMounts { volumesInSidecars = append(volumesInSidecars, *volumesMap[mount.Name]) } + for _, mount := range sidecarContainer.VolumeDevices { + volumesInSidecars = append(volumesInSidecars, *volumesMap[mount.Name]) + } // merge VolumeMounts from sidecar.VolumeMounts and shared VolumeMounts - sidecarContainer.VolumeMounts = util.MergeVolumeMounts(sidecarContainer.VolumeMounts, injectedMounts) + sidecarContainer.VolumeMounts = util.MergeVolumeMounts(sidecarContainer.Container, injectedMounts) // add the "Injected" env to the sidecar container sidecarContainer.Env = append(sidecarContainer.Env, corev1.EnvVar{Name: sidecarcontrol.SidecarEnvKey, Value: "true"}) // merged Env from sidecar.Env and transfer envs sidecarContainer.Env = util.MergeEnvVar(sidecarContainer.Env, transferEnvs) + // merge volumeDevice + injectedDevices := sidecarcontrol.GetInjectedVolumeDevices(sidecarContainer, pod) + sidecarContainer.VolumeDevices = util.MergeVolumeDevices(sidecarContainer.Container, injectedDevices) + klog.InfoS("try to inject Container sidecar", + "containerName", sidecarContainer.Name, "namespace", pod.Namespace, "podName", pod.Name, "envs", transferEnvs, "volumeMounts", injectedMounts, "volumeDevices", injectedDevices) // when sidecar container UpgradeStrategy is HotUpgrade if sidecarcontrol.IsHotUpgradeContainer(sidecarContainer) { hotContainers, annotations := injectHotUpgradeContainers(hotUpgradeWorkInfo, sidecarContainer) diff --git a/pkg/webhook/pod/mutating/sidecarset_test.go b/pkg/webhook/pod/mutating/sidecarset_test.go index 53dfc1a5e..99de8f150 100644 --- a/pkg/webhook/pod/mutating/sidecarset_test.go +++ b/pkg/webhook/pod/mutating/sidecarset_test.go @@ -1647,3 +1647,196 @@ func newAdmissionRequest(op admissionv1.Operation, object, oldObject runtime.Raw SubResource: subResource, } } + +func TestPodVolumeDevicesAppend(t *testing.T) { + cases := []struct { + name string + getPod func() *corev1.Pod + getSidecarSet func() *appsv1alpha1.SidecarSet + exceptInitVolumeDevices []corev1.VolumeDevice + exceptVolumeDevices []corev1.VolumeDevice + exceptVolumes []corev1.Volume + }{ + { + name: "append normal volumeDevices, ShareVolumeDevicePolicy=disable", + getPod: func() *corev1.Pod { + // /a/b、/e/f + podIn := podWithStaragent.DeepCopy() + podIn.Spec.Containers[0].VolumeDevices = []corev1.VolumeDevice{ + { + Name: "origin-pvc-1", + DevicePath: "/origin-1", + }, + { + Name: "origin-pvc-share", + DevicePath: "/share", + }, + } + podIn.Spec.Volumes = append(podIn.Spec.Volumes, corev1.Volume{Name: "origin-pvc-1"}) + podIn.Spec.Volumes = append(podIn.Spec.Volumes, corev1.Volume{Name: "origin-pvc-share"}) + return podIn + }, + getSidecarSet: func() *appsv1alpha1.SidecarSet { + sidecarSetIn := sidecarSetWithStaragent.DeepCopy() + sidecarSetIn.Spec.Containers = sidecarSetIn.Spec.Containers[:1] + sidecarSetIn.Spec.InitContainers[0].VolumeDevices = []corev1.VolumeDevice{ + { + Name: "disk0", + DevicePath: "/dev/nvme0", + }, + } + sidecarSetIn.Spec.InitContainers[0].VolumeMounts = nil + sidecarSetIn.Spec.InitContainers[0].ShareVolumePolicy = appsv1alpha1.ShareVolumePolicy{} + sidecarSetIn.Spec.Containers[0].VolumeDevices = []corev1.VolumeDevice{ + { + Name: "disk0", + DevicePath: "/dev/nvme1", + }, + } + sidecarSetIn.Spec.Containers[0].VolumeMounts = nil + sidecarSetIn.Spec.Containers[0].ShareVolumePolicy = appsv1alpha1.ShareVolumePolicy{} + sidecarSetIn.Spec.Volumes = []corev1.Volume{{Name: "disk0"}} + + return sidecarSetIn + }, + exceptInitVolumeDevices: []corev1.VolumeDevice{ + { + Name: "disk0", + DevicePath: "/dev/nvme0", + }, + }, + exceptVolumeDevices: []corev1.VolumeDevice{ + { + Name: "disk0", + DevicePath: "/dev/nvme1", + }, + }, + exceptVolumes: []corev1.Volume{ + {Name: "volume-a"}, + {Name: "volume-b"}, + {Name: "volume-staragent"}, + {Name: "origin-pvc-1"}, + {Name: "origin-pvc-share"}, + {Name: "disk0"}, + }, + }, + { + name: "append normal volumeDevices, ShareVolumeDevicePolicy=enable", + getPod: func() *corev1.Pod { + // /a/b、/e/f + podIn := podWithStaragent.DeepCopy() + podIn.Spec.Containers[0].VolumeDevices = []corev1.VolumeDevice{ + { + Name: "origin-pvc-1", + DevicePath: "/origin-1", + }, + { + Name: "origin-pvc-share", + DevicePath: "/share", + }, + } + podIn.Spec.Volumes = append(podIn.Spec.Volumes, corev1.Volume{Name: "origin-pvc-1"}) + podIn.Spec.Volumes = append(podIn.Spec.Volumes, corev1.Volume{Name: "origin-pvc-share"}) + return podIn + }, + getSidecarSet: func() *appsv1alpha1.SidecarSet { + sidecarSetIn := sidecarSetWithStaragent.DeepCopy() + sidecarSetIn.Spec.Containers = sidecarSetIn.Spec.Containers[:1] + sidecarSetIn.Spec.InitContainers[0].VolumeDevices = []corev1.VolumeDevice{ + { + Name: "disk0", + DevicePath: "/dev/nvme0", + }, + } + sidecarSetIn.Spec.InitContainers[0].ShareVolumePolicy = appsv1alpha1.ShareVolumePolicy{} + sidecarSetIn.Spec.InitContainers[0].ShareVolumeDevicePolicy = &appsv1alpha1.ShareVolumePolicy{ + Type: appsv1alpha1.ShareVolumePolicyEnabled, + } + sidecarSetIn.Spec.Containers[0].VolumeDevices = []corev1.VolumeDevice{ + { + Name: "disk0", + DevicePath: "/dev/nvme1", + }, + } + sidecarSetIn.Spec.Containers[0].VolumeMounts = nil + sidecarSetIn.Spec.Containers[0].ShareVolumePolicy = appsv1alpha1.ShareVolumePolicy{} + sidecarSetIn.Spec.Containers[0].ShareVolumeDevicePolicy = &appsv1alpha1.ShareVolumePolicy{ + Type: appsv1alpha1.ShareVolumePolicyEnabled, + } + sidecarSetIn.Spec.Volumes = []corev1.Volume{ + {Name: "volume-3"}, + {Name: "volume-4"}, + {Name: "volume-staragent"}, + {Name: "disk0"}, + } + + return sidecarSetIn + }, + exceptInitVolumeDevices: []corev1.VolumeDevice{ + { + Name: "disk0", + DevicePath: "/dev/nvme0", + }, + { + Name: "origin-pvc-1", + DevicePath: "/origin-1", + }, + { + Name: "origin-pvc-share", + DevicePath: "/share", + }, + }, + exceptVolumeDevices: []corev1.VolumeDevice{ + { + Name: "disk0", + DevicePath: "/dev/nvme1", + }, + { + Name: "origin-pvc-1", + DevicePath: "/origin-1", + }, + { + Name: "origin-pvc-share", + DevicePath: "/share", + }, + }, + exceptVolumes: []corev1.Volume{ + {Name: "volume-a"}, + {Name: "volume-b"}, + {Name: "volume-staragent"}, + {Name: "origin-pvc-1"}, + {Name: "origin-pvc-share"}, + {Name: "volume-3"}, + {Name: "volume-4"}, + {Name: "disk0"}, + }, + }, + } + + for _, cs := range cases { + t.Run(cs.name, func(t *testing.T) { + podIn := cs.getPod() + decoder := admission.NewDecoder(scheme.Scheme) + c := fake.NewClientBuilder().WithObjects(cs.getSidecarSet()).WithIndex( + &appsv1alpha1.SidecarSet{}, fieldindex.IndexNameForSidecarSetNamespace, fieldindex.IndexSidecarSet, + ).Build() + podOut := podIn.DeepCopy() + podHandler := &PodCreateHandler{Decoder: decoder, Client: c} + req := newAdmission(admissionv1.Create, runtime.RawExtension{}, runtime.RawExtension{}, "") + _, err := podHandler.sidecarsetMutatingPod(context.Background(), req, podOut) + if err != nil { + t.Fatalf("inject sidecar into pod failed, err: %v", err) + } + + if !reflect.DeepEqual(cs.exceptInitVolumeDevices, podOut.Spec.InitContainers[0].VolumeDevices) { + t.Fatalf("expect(%v), but get(%v)", cs.exceptInitVolumeDevices, podOut.Spec.InitContainers[0].VolumeDevices) + } + if !reflect.DeepEqual(cs.exceptVolumeDevices, podOut.Spec.Containers[1].VolumeDevices) { + t.Fatalf("expect(%v), but get(%v)", cs.exceptVolumeDevices, podOut.Spec.Containers[1].VolumeDevices) + } + if !reflect.DeepEqual(cs.exceptVolumes, podOut.Spec.Volumes) { + t.Fatalf("expect(%v), but get(%v)", util.DumpJSON(cs.exceptVolumes), util.DumpJSON(podOut.Spec.Volumes)) + } + }) + } +} diff --git a/pkg/webhook/sidecarset/validating/sidecarset_validating_test.go b/pkg/webhook/sidecarset/validating/sidecarset_validating_test.go index ae9999819..56a41577d 100644 --- a/pkg/webhook/sidecarset/validating/sidecarset_validating_test.go +++ b/pkg/webhook/sidecarset/validating/sidecarset_validating_test.go @@ -379,6 +379,152 @@ func TestValidateSidecarSet(t *testing.T) { }, expectErrs: 1, }, + { + caseName: "wrong-volumeDevice-volumes", + sidecarSet: appsv1alpha1.SidecarSet{ + ObjectMeta: metav1.ObjectMeta{Name: "test-sidecarset"}, + Spec: appsv1alpha1.SidecarSetSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + UpdateStrategy: appsv1alpha1.SidecarSetUpdateStrategy{ + Type: appsv1alpha1.NotUpdateSidecarSetStrategyType, + }, + Containers: []appsv1alpha1.SidecarContainer{ + { + PodInjectPolicy: appsv1alpha1.BeforeAppContainerType, + ShareVolumePolicy: appsv1alpha1.ShareVolumePolicy{ + Type: appsv1alpha1.ShareVolumePolicyDisabled, + }, + UpgradeStrategy: appsv1alpha1.SidecarContainerUpgradeStrategy{ + UpgradeType: appsv1alpha1.SidecarContainerColdUpgrade, + }, + Container: corev1.Container{ + Name: "test-sidecar", + Image: "test-image", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageReadFile, + VolumeDevices: []corev1.VolumeDevice{ + { + Name: "disk0", + DevicePath: "/dev/nvme1", + }, + }, + }, + }, + }, + }, + }, + expectErrs: 1, + }, + { + caseName: "right-volumeDevice-volumes", + sidecarSet: appsv1alpha1.SidecarSet{ + ObjectMeta: metav1.ObjectMeta{Name: "test-sidecarset"}, + Spec: appsv1alpha1.SidecarSetSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + UpdateStrategy: appsv1alpha1.SidecarSetUpdateStrategy{ + Type: appsv1alpha1.NotUpdateSidecarSetStrategyType, + }, + Containers: []appsv1alpha1.SidecarContainer{ + { + PodInjectPolicy: appsv1alpha1.BeforeAppContainerType, + ShareVolumePolicy: appsv1alpha1.ShareVolumePolicy{ + Type: appsv1alpha1.ShareVolumePolicyDisabled, + }, + UpgradeStrategy: appsv1alpha1.SidecarContainerUpgradeStrategy{ + UpgradeType: appsv1alpha1.SidecarContainerColdUpgrade, + }, + Container: corev1.Container{ + Name: "test-sidecar", + Image: "test-image", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageReadFile, + VolumeDevices: []corev1.VolumeDevice{ + { + Name: "disk0", + DevicePath: "/dev/nvme1", + }, + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "disk0", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "pvc-0", + }, + }, + }, + }, + }, + }, + expectErrs: 0, + }, + { + caseName: "volumeDevice-volumeMount-path-conflict", + sidecarSet: appsv1alpha1.SidecarSet{ + ObjectMeta: metav1.ObjectMeta{Name: "test-sidecarset"}, + Spec: appsv1alpha1.SidecarSetSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"a": "b"}, + }, + UpdateStrategy: appsv1alpha1.SidecarSetUpdateStrategy{ + Type: appsv1alpha1.NotUpdateSidecarSetStrategyType, + }, + Containers: []appsv1alpha1.SidecarContainer{ + { + PodInjectPolicy: appsv1alpha1.BeforeAppContainerType, + ShareVolumePolicy: appsv1alpha1.ShareVolumePolicy{ + Type: appsv1alpha1.ShareVolumePolicyDisabled, + }, + UpgradeStrategy: appsv1alpha1.SidecarContainerUpgradeStrategy{ + UpgradeType: appsv1alpha1.SidecarContainerColdUpgrade, + }, + Container: corev1.Container{ + Name: "test-sidecar", + Image: "test-image", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageReadFile, + VolumeDevices: []corev1.VolumeDevice{ + { + Name: "disk0", + DevicePath: "/home/admin/log", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "log", + MountPath: "/home/admin/log", + }, + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "disk0", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "pvc-0", + }, + }, + }, + { + Name: "log", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + }, + }, + expectErrs: 2, + }, { caseName: "wrong-metadata", sidecarSet: appsv1alpha1.SidecarSet{