sidecarset support shareVolumeDevicePolicy (#2011)

Signed-off-by: liheng.zms <liheng.zms@alibaba-inc.com>
This commit is contained in:
berg 2025-05-07 13:15:16 +08:00 committed by GitHub
parent d8bf9c9b53
commit d65527ea66
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 718 additions and 46 deletions

View File

@ -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"`

View File

@ -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))

View File

@ -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

View File

@ -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

View File

@ -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)
}
})
}
}

View File

@ -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

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)

View File

@ -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))
}
})
}
}

View File

@ -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{