Add ImageVolumeSource API
Adding the required Kubernetes API so that the kubelet can start using it. This patch also adds the corresponding alpha feature gate as outlined in KEP 4639. Signed-off-by: Sascha Grunert <sgrunert@redhat.com>
This commit is contained in:
@@ -385,6 +385,7 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po
|
||||
AllowInvalidTopologySpreadConstraintLabelSelector: false,
|
||||
AllowNamespacedSysctlsForHostNetAndHostIPC: false,
|
||||
AllowNonLocalProjectedTokenPath: false,
|
||||
AllowImageVolumeSource: utilfeature.DefaultFeatureGate.Enabled(features.ImageVolume),
|
||||
}
|
||||
|
||||
// If old spec uses relaxed validation or enabled the RelaxedEnvironmentVariableValidation feature gate,
|
||||
@@ -713,6 +714,7 @@ func dropDisabledFields(
|
||||
}
|
||||
|
||||
dropPodLifecycleSleepAction(podSpec, oldPodSpec)
|
||||
dropImageVolumes(podSpec, oldPodSpec)
|
||||
}
|
||||
|
||||
func dropPodLifecycleSleepAction(podSpec, oldPodSpec *api.PodSpec) {
|
||||
@@ -1260,3 +1262,56 @@ func MarkPodProposedForResize(oldPod, newPod *api.Pod) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// KEP: https://kep.k8s.io/4639
|
||||
func dropImageVolumes(podSpec, oldPodSpec *api.PodSpec) {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ImageVolume) || imageVolumesInUse(oldPodSpec) {
|
||||
return
|
||||
}
|
||||
|
||||
imageVolumeNames := sets.New[string]()
|
||||
var newVolumes []api.Volume
|
||||
for _, v := range podSpec.Volumes {
|
||||
if v.Image != nil {
|
||||
imageVolumeNames.Insert(v.Name)
|
||||
continue
|
||||
}
|
||||
newVolumes = append(newVolumes, v)
|
||||
}
|
||||
podSpec.Volumes = newVolumes
|
||||
|
||||
dropVolumeMounts := func(givenMounts []api.VolumeMount) (newVolumeMounts []api.VolumeMount) {
|
||||
for _, m := range givenMounts {
|
||||
if !imageVolumeNames.Has(m.Name) {
|
||||
newVolumeMounts = append(newVolumeMounts, m)
|
||||
}
|
||||
}
|
||||
return newVolumeMounts
|
||||
}
|
||||
|
||||
for i, c := range podSpec.Containers {
|
||||
podSpec.Containers[i].VolumeMounts = dropVolumeMounts(c.VolumeMounts)
|
||||
}
|
||||
|
||||
for i, c := range podSpec.InitContainers {
|
||||
podSpec.InitContainers[i].VolumeMounts = dropVolumeMounts(c.VolumeMounts)
|
||||
}
|
||||
|
||||
for i, c := range podSpec.EphemeralContainers {
|
||||
podSpec.EphemeralContainers[i].VolumeMounts = dropVolumeMounts(c.VolumeMounts)
|
||||
}
|
||||
}
|
||||
|
||||
func imageVolumesInUse(podSpec *api.PodSpec) bool {
|
||||
if podSpec == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, v := range podSpec.Volumes {
|
||||
if v.Image != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -3574,3 +3574,156 @@ func TestDropSupplementalGroupsPolicy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDropImageVolumes(t *testing.T) {
|
||||
const (
|
||||
volumeNameImage = "volume"
|
||||
volumeNameOther = "volume-other"
|
||||
)
|
||||
anotherVolume := api.Volume{Name: volumeNameOther, VolumeSource: api.VolumeSource{HostPath: &api.HostPathVolumeSource{}}}
|
||||
podWithVolume := &api.Pod{
|
||||
Spec: api.PodSpec{
|
||||
Volumes: []api.Volume{
|
||||
{Name: volumeNameImage, VolumeSource: api.VolumeSource{Image: &api.ImageVolumeSource{}}},
|
||||
anotherVolume,
|
||||
},
|
||||
Containers: []api.Container{{
|
||||
VolumeMounts: []api.VolumeMount{{Name: volumeNameImage}, {Name: volumeNameOther}},
|
||||
}},
|
||||
InitContainers: []api.Container{{
|
||||
VolumeMounts: []api.VolumeMount{{Name: volumeNameImage}},
|
||||
}},
|
||||
EphemeralContainers: []api.EphemeralContainer{
|
||||
{EphemeralContainerCommon: api.EphemeralContainerCommon{
|
||||
VolumeMounts: []api.VolumeMount{{Name: volumeNameImage}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
podWithoutVolume := &api.Pod{
|
||||
Spec: api.PodSpec{
|
||||
Volumes: []api.Volume{anotherVolume},
|
||||
Containers: []api.Container{{VolumeMounts: []api.VolumeMount{{Name: volumeNameOther}}}},
|
||||
InitContainers: []api.Container{{}},
|
||||
EphemeralContainers: []api.EphemeralContainer{{}},
|
||||
},
|
||||
}
|
||||
|
||||
noPod := &api.Pod{}
|
||||
|
||||
testcases := []struct {
|
||||
description string
|
||||
enabled bool
|
||||
oldPod *api.Pod
|
||||
newPod *api.Pod
|
||||
wantPod *api.Pod
|
||||
}{
|
||||
{
|
||||
description: "old with volume / new with volume / disabled",
|
||||
oldPod: podWithVolume,
|
||||
newPod: podWithVolume,
|
||||
wantPod: podWithVolume,
|
||||
},
|
||||
{
|
||||
description: "old without volume / new with volume / disabled",
|
||||
oldPod: podWithoutVolume,
|
||||
newPod: podWithVolume,
|
||||
wantPod: podWithoutVolume,
|
||||
},
|
||||
{
|
||||
description: "no old pod/ new with volume / disabled",
|
||||
oldPod: noPod,
|
||||
newPod: podWithVolume,
|
||||
wantPod: podWithoutVolume,
|
||||
},
|
||||
{
|
||||
description: "nil old pod/ new with volume / disabled",
|
||||
oldPod: nil,
|
||||
newPod: podWithVolume,
|
||||
wantPod: podWithoutVolume,
|
||||
},
|
||||
{
|
||||
description: "old with volume / new without volume / disabled",
|
||||
oldPod: podWithVolume,
|
||||
newPod: podWithoutVolume,
|
||||
wantPod: podWithoutVolume,
|
||||
},
|
||||
{
|
||||
description: "old without volume / new without volume / disabled",
|
||||
oldPod: podWithoutVolume,
|
||||
newPod: podWithoutVolume,
|
||||
wantPod: podWithoutVolume,
|
||||
},
|
||||
{
|
||||
description: "no old pod/ new without volume / disabled",
|
||||
oldPod: noPod,
|
||||
newPod: podWithoutVolume,
|
||||
wantPod: podWithoutVolume,
|
||||
},
|
||||
|
||||
{
|
||||
description: "old with volume / new with volume / enabled",
|
||||
enabled: true,
|
||||
oldPod: podWithVolume,
|
||||
newPod: podWithVolume,
|
||||
wantPod: podWithVolume,
|
||||
},
|
||||
{
|
||||
description: "old without volume / new with volume / enabled",
|
||||
enabled: true,
|
||||
oldPod: podWithoutVolume,
|
||||
newPod: podWithVolume,
|
||||
wantPod: podWithVolume,
|
||||
},
|
||||
{
|
||||
description: "no old pod/ new with volume / enabled",
|
||||
enabled: true,
|
||||
oldPod: noPod,
|
||||
newPod: podWithVolume,
|
||||
wantPod: podWithVolume,
|
||||
},
|
||||
|
||||
{
|
||||
description: "old with volume / new without volume / enabled",
|
||||
enabled: true,
|
||||
oldPod: podWithVolume,
|
||||
newPod: podWithoutVolume,
|
||||
wantPod: podWithoutVolume,
|
||||
},
|
||||
{
|
||||
description: "old without volume / new without volume / enabled",
|
||||
enabled: true,
|
||||
oldPod: podWithoutVolume,
|
||||
newPod: podWithoutVolume,
|
||||
wantPod: podWithoutVolume,
|
||||
},
|
||||
{
|
||||
description: "no old pod/ new without volume / enabled",
|
||||
enabled: true,
|
||||
oldPod: noPod,
|
||||
newPod: podWithoutVolume,
|
||||
wantPod: podWithoutVolume,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ImageVolume, tc.enabled)
|
||||
|
||||
oldPod := tc.oldPod.DeepCopy()
|
||||
newPod := tc.newPod.DeepCopy()
|
||||
wantPod := tc.wantPod
|
||||
DropDisabledPodFields(newPod, oldPod)
|
||||
|
||||
// old pod should never be changed
|
||||
if diff := cmp.Diff(oldPod, tc.oldPod); diff != "" {
|
||||
t.Errorf("old pod changed: %s", diff)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(wantPod, newPod); diff != "" {
|
||||
t.Errorf("new pod changed (- want, + got): %s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user