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:
Sascha Grunert
2024-06-24 10:34:43 +02:00
parent ad72be434d
commit f7ca3131e0
86 changed files with 2395 additions and 1167 deletions

View File

@@ -73,6 +73,13 @@ func (a *AlwaysPullImages) Admit(ctx context.Context, attributes admission.Attri
return true
})
// See: https://kep.k8s.io/4639
for _, v := range pod.Spec.Volumes {
if v.Image != nil {
v.Image.PullPolicy = api.PullAlways
}
}
return nil
}
@@ -96,6 +103,20 @@ func (*AlwaysPullImages) Validate(ctx context.Context, attributes admission.Attr
}
return true
})
// See: https://kep.k8s.io/4639
for i, v := range pod.Spec.Volumes {
if v.Image != nil && v.Image.PullPolicy != api.PullAlways {
allErrs = append(allErrs, admission.NewForbidden(attributes,
field.NotSupported(
field.NewPath("spec").Child("volumes").Index(i).Child("image").Child("pullPolicy"),
v.Image.PullPolicy,
[]string{string(api.PullAlways)},
),
))
}
}
if len(allErrs) > 0 {
return utilerrors.NewAggregate(allErrs)
}

View File

@@ -47,6 +47,11 @@ func TestAdmission(t *testing.T) {
{Name: "ctr3", Image: "image", ImagePullPolicy: api.PullIfNotPresent},
{Name: "ctr4", Image: "image", ImagePullPolicy: api.PullAlways},
},
Volumes: []api.Volume{
{Name: "volume1", VolumeSource: api.VolumeSource{Image: &api.ImageVolumeSource{PullPolicy: api.PullNever}}},
{Name: "volume2", VolumeSource: api.VolumeSource{Image: &api.ImageVolumeSource{PullPolicy: api.PullIfNotPresent}}},
{Name: "volume3", VolumeSource: api.VolumeSource{Image: &api.ImageVolumeSource{PullPolicy: api.PullAlways}}},
},
},
}
err := handler.Admit(context.TODO(), admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
@@ -63,6 +68,11 @@ func TestAdmission(t *testing.T) {
t.Errorf("Container %v: expected pull always, got %v", c, c.ImagePullPolicy)
}
}
for _, v := range pod.Spec.Volumes {
if v.Image.PullPolicy != api.PullAlways {
t.Errorf("Image volume %v: expected pull always, got %v", v, v.Image.PullPolicy)
}
}
}
func TestValidate(t *testing.T) {
@@ -83,6 +93,12 @@ func TestValidate(t *testing.T) {
{Name: "ctr3", Image: "image", ImagePullPolicy: api.PullIfNotPresent},
{Name: "ctr4", Image: "image", ImagePullPolicy: api.PullAlways},
},
Volumes: []api.Volume{
{Name: "volume1", VolumeSource: api.VolumeSource{Image: &api.ImageVolumeSource{PullPolicy: ""}}},
{Name: "volume2", VolumeSource: api.VolumeSource{Image: &api.ImageVolumeSource{PullPolicy: api.PullNever}}},
{Name: "volume3", VolumeSource: api.VolumeSource{Image: &api.ImageVolumeSource{PullPolicy: api.PullIfNotPresent}}},
{Name: "volume4", VolumeSource: api.VolumeSource{Image: &api.ImageVolumeSource{PullPolicy: api.PullAlways}}},
},
},
}
expectedError := `[` +
@@ -91,7 +107,11 @@ func TestValidate(t *testing.T) {
`pods "123" is forbidden: spec.initContainers[2].imagePullPolicy: Unsupported value: "IfNotPresent": supported values: "Always", ` +
`pods "123" is forbidden: spec.containers[0].imagePullPolicy: Unsupported value: "": supported values: "Always", ` +
`pods "123" is forbidden: spec.containers[1].imagePullPolicy: Unsupported value: "Never": supported values: "Always", ` +
`pods "123" is forbidden: spec.containers[2].imagePullPolicy: Unsupported value: "IfNotPresent": supported values: "Always"]`
`pods "123" is forbidden: spec.containers[2].imagePullPolicy: Unsupported value: "IfNotPresent": supported values: "Always", ` +
`pods "123" is forbidden: spec.volumes[0].image.pullPolicy: Unsupported value: "": supported values: "Always", ` +
`pods "123" is forbidden: spec.volumes[1].image.pullPolicy: Unsupported value: "Never": supported values: "Always", ` +
`pods "123" is forbidden: spec.volumes[2].image.pullPolicy: Unsupported value: "IfNotPresent": supported values: "Always"` +
`]`
err := handler.Validate(context.TODO(), admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err == nil {
t.Fatal("missing expected error")