Skip to content

Commit

Permalink
Add ImageVolumeSource API
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
saschagrunert committed Jul 18, 2024
1 parent ad72be4 commit f7ca313
Show file tree
Hide file tree
Showing 86 changed files with 2,395 additions and 1,167 deletions.
18 changes: 18 additions & 0 deletions api/openapi-spec/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions api/openapi-spec/v3/api__v1_openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2980,6 +2980,20 @@
],
"type": "object"
},
"io.k8s.api.core.v1.ImageVolumeSource": {
"description": "ImageVolumeSource represents a image volume resource.",
"properties": {
"pullPolicy": {
"description": "Policy for pulling OCI objects. Possible values are: Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise.",
"type": "string"
},
"reference": {
"description": "Required: Image or artifact reference to be used. Behaves in the same way as pod.spec.containers[*].image. Pull secrets will be assembled in the same way as for the container image by looking up node credentials, SA image pull secrets, and pod spec image pull secrets. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.",
"type": "string"
}
},
"type": "object"
},
"io.k8s.api.core.v1.KeyToPath": {
"description": "Maps a string key to a path within a volume.",
"properties": {
Expand Down Expand Up @@ -8131,6 +8145,14 @@
],
"description": "hostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath"
},
"image": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.api.core.v1.ImageVolumeSource"
}
],
"description": "image represents an OCI object (a container image or artifact) pulled and mounted on the kubelet's host machine. The volume is resolved at pod startup depending on which PullPolicy value is provided:\n\n- Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. - Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. - IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails.\n\nThe volume gets re-resolved if the pod gets deleted and recreated, which means that new remote content will become available on pod recreation. A failure to resolve or pull the image during pod startup will block containers from starting and may add significant latency. Failures will be retried using normal volume backoff and will be reported on the pod reason and message. The types of objects that may be mounted by this volume are defined by the container runtime implementation on a host machine and at minimum must include all valid types supported by the container image field. The OCI object gets mounted in a single directory (spec.containers[*].volumeMounts.mountPath) by merging the manifest layers in the same way as for container images. The volume will be mounted read-only (ro) and non-executable files (noexec). Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath)."
},
"iscsi": {
"allOf": [
{
Expand Down
22 changes: 22 additions & 0 deletions api/openapi-spec/v3/apis__apps__v1_openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2806,6 +2806,20 @@
],
"type": "object"
},
"io.k8s.api.core.v1.ImageVolumeSource": {
"description": "ImageVolumeSource represents a image volume resource.",
"properties": {
"pullPolicy": {
"description": "Policy for pulling OCI objects. Possible values are: Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise.",
"type": "string"
},
"reference": {
"description": "Required: Image or artifact reference to be used. Behaves in the same way as pod.spec.containers[*].image. Pull secrets will be assembled in the same way as for the container image by looking up node credentials, SA image pull secrets, and pod spec image pull secrets. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.",
"type": "string"
}
},
"type": "object"
},
"io.k8s.api.core.v1.KeyToPath": {
"description": "Maps a string key to a path within a volume.",
"properties": {
Expand Down Expand Up @@ -5022,6 +5036,14 @@
],
"description": "hostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath"
},
"image": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.api.core.v1.ImageVolumeSource"
}
],
"description": "image represents an OCI object (a container image or artifact) pulled and mounted on the kubelet's host machine. The volume is resolved at pod startup depending on which PullPolicy value is provided:\n\n- Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. - Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. - IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails.\n\nThe volume gets re-resolved if the pod gets deleted and recreated, which means that new remote content will become available on pod recreation. A failure to resolve or pull the image during pod startup will block containers from starting and may add significant latency. Failures will be retried using normal volume backoff and will be reported on the pod reason and message. The types of objects that may be mounted by this volume are defined by the container runtime implementation on a host machine and at minimum must include all valid types supported by the container image field. The OCI object gets mounted in a single directory (spec.containers[*].volumeMounts.mountPath) by merging the manifest layers in the same way as for container images. The volume will be mounted read-only (ro) and non-executable files (noexec). Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath)."
},
"iscsi": {
"allOf": [
{
Expand Down
22 changes: 22 additions & 0 deletions api/openapi-spec/v3/apis__batch__v1_openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2155,6 +2155,20 @@
],
"type": "object"
},
"io.k8s.api.core.v1.ImageVolumeSource": {
"description": "ImageVolumeSource represents a image volume resource.",
"properties": {
"pullPolicy": {
"description": "Policy for pulling OCI objects. Possible values are: Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise.",
"type": "string"
},
"reference": {
"description": "Required: Image or artifact reference to be used. Behaves in the same way as pod.spec.containers[*].image. Pull secrets will be assembled in the same way as for the container image by looking up node credentials, SA image pull secrets, and pod spec image pull secrets. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.",
"type": "string"
}
},
"type": "object"
},
"io.k8s.api.core.v1.KeyToPath": {
"description": "Maps a string key to a path within a volume.",
"properties": {
Expand Down Expand Up @@ -4226,6 +4240,14 @@
],
"description": "hostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath"
},
"image": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.api.core.v1.ImageVolumeSource"
}
],
"description": "image represents an OCI object (a container image or artifact) pulled and mounted on the kubelet's host machine. The volume is resolved at pod startup depending on which PullPolicy value is provided:\n\n- Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. - Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. - IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails.\n\nThe volume gets re-resolved if the pod gets deleted and recreated, which means that new remote content will become available on pod recreation. A failure to resolve or pull the image during pod startup will block containers from starting and may add significant latency. Failures will be retried using normal volume backoff and will be reported on the pod reason and message. The types of objects that may be mounted by this volume are defined by the container runtime implementation on a host machine and at minimum must include all valid types supported by the container image field. The OCI object gets mounted in a single directory (spec.containers[*].volumeMounts.mountPath) by merging the manifest layers in the same way as for container images. The volume will be mounted read-only (ro) and non-executable files (noexec). Sub path mounts for containers are not supported (spec.containers[*].volumeMounts.subpath)."
},
"iscsi": {
"allOf": [
{
Expand Down
55 changes: 55 additions & 0 deletions pkg/api/pod/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -713,6 +714,7 @@ func dropDisabledFields(
}

dropPodLifecycleSleepAction(podSpec, oldPodSpec)
dropImageVolumes(podSpec, oldPodSpec)
}

func dropPodLifecycleSleepAction(podSpec, oldPodSpec *api.PodSpec) {
Expand Down Expand Up @@ -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
}
Loading

0 comments on commit f7ca313

Please sign in to comment.