mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 04:33:26 +00:00
reset fields when the feature gate was not set
This commit is contained in:
parent
2253b53b58
commit
3790ee2fe8
@ -813,6 +813,17 @@ func dropDisabledPodStatusFields(podStatus, oldPodStatus *api.PodStatus, podSpec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !utilfeature.DefaultFeatureGate.Enabled(features.ResourceHealthStatus) {
|
||||||
|
setAllocatedResourcesStatusToNil := func(csl []api.ContainerStatus) {
|
||||||
|
for i := range csl {
|
||||||
|
csl[i].AllocatedResourcesStatus = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setAllocatedResourcesStatusToNil(podStatus.ContainerStatuses)
|
||||||
|
setAllocatedResourcesStatusToNil(podStatus.InitContainerStatuses)
|
||||||
|
setAllocatedResourcesStatusToNil(podStatus.EphemeralContainerStatuses)
|
||||||
|
}
|
||||||
|
|
||||||
// drop ContainerStatus.User field to empty (disable SupplementalGroupsPolicy)
|
// drop ContainerStatus.User field to empty (disable SupplementalGroupsPolicy)
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.SupplementalGroupsPolicy) && !supplementalGroupsPolicyInUse(oldPodSpec) {
|
if !utilfeature.DefaultFeatureGate.Enabled(features.SupplementalGroupsPolicy) && !supplementalGroupsPolicyInUse(oldPodSpec) {
|
||||||
dropUserField := func(csl []api.ContainerStatus) {
|
dropUserField := func(csl []api.ContainerStatus) {
|
||||||
@ -1161,6 +1172,22 @@ func rroInUse(podSpec *api.PodSpec) bool {
|
|||||||
return inUse
|
return inUse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func allocatedResourcesStatusInUse(podSpec *api.PodStatus) bool {
|
||||||
|
if podSpec == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
inUse := func(csl []api.ContainerStatus) bool {
|
||||||
|
for _, cs := range csl {
|
||||||
|
if len(cs.AllocatedResourcesStatus) > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return inUse(podSpec.ContainerStatuses) || inUse(podSpec.InitContainerStatuses) || inUse(podSpec.EphemeralContainerStatuses)
|
||||||
|
}
|
||||||
|
|
||||||
func dropDisabledClusterTrustBundleProjection(podSpec, oldPodSpec *api.PodSpec) {
|
func dropDisabledClusterTrustBundleProjection(podSpec, oldPodSpec *api.PodSpec) {
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.ClusterTrustBundleProjection) {
|
if utilfeature.DefaultFeatureGate.Enabled(features.ClusterTrustBundleProjection) {
|
||||||
return
|
return
|
||||||
|
@ -5378,6 +5378,11 @@ func ValidatePodStatusUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions
|
|||||||
allErrs = append(allErrs, validateContainerStatusUsers(newPod.Status.InitContainerStatuses, fldPath.Child("initContainerStatuses"), newPod.Spec.OS)...)
|
allErrs = append(allErrs, validateContainerStatusUsers(newPod.Status.InitContainerStatuses, fldPath.Child("initContainerStatuses"), newPod.Spec.OS)...)
|
||||||
allErrs = append(allErrs, validateContainerStatusUsers(newPod.Status.EphemeralContainerStatuses, fldPath.Child("ephemeralContainerStatuses"), newPod.Spec.OS)...)
|
allErrs = append(allErrs, validateContainerStatusUsers(newPod.Status.EphemeralContainerStatuses, fldPath.Child("ephemeralContainerStatuses"), newPod.Spec.OS)...)
|
||||||
|
|
||||||
|
allErrs = append(allErrs, validateContainerStatusAllocatedResourcesStatus(newPod.Status.ContainerStatuses, fldPath.Child("containerStatuses"), newPod.Spec.Containers)...)
|
||||||
|
allErrs = append(allErrs, validateContainerStatusAllocatedResourcesStatus(newPod.Status.InitContainerStatuses, fldPath.Child("initContainerStatuses"), newPod.Spec.InitContainers)...)
|
||||||
|
// ephemeral containers are not allowed to have resources allocated
|
||||||
|
allErrs = append(allErrs, validateContainerStatusNoAllocatedResourcesStatus(newPod.Status.EphemeralContainerStatuses, fldPath.Child("ephemeralContainerStatuses"))...)
|
||||||
|
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8200,6 +8205,80 @@ func validateContainerStatusUsers(containerStatuses []core.ContainerStatus, fldP
|
|||||||
return allErrors
|
return allErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateContainerStatusNoAllocatedResourcesStatus(containerStatuses []core.ContainerStatus, fldPath *field.Path) field.ErrorList {
|
||||||
|
allErrors := field.ErrorList{}
|
||||||
|
|
||||||
|
for i, containerStatus := range containerStatuses {
|
||||||
|
if containerStatus.AllocatedResourcesStatus == nil {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
allErrors = append(allErrors, field.Forbidden(fldPath.Index(i).Child("allocatedResourcesStatus"), "cannot be set for a container status"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allErrors
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateContainerStatusAllocatedResourcesStatus iterate the allocated resources health and validate:
|
||||||
|
// - resourceName matches one of resources in container's resource requirements
|
||||||
|
// - resourceID is not empty and unique
|
||||||
|
func validateContainerStatusAllocatedResourcesStatus(containerStatuses []core.ContainerStatus, fldPath *field.Path, containers []core.Container) field.ErrorList {
|
||||||
|
allErrors := field.ErrorList{}
|
||||||
|
|
||||||
|
for i, containerStatus := range containerStatuses {
|
||||||
|
if containerStatus.AllocatedResourcesStatus == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
allocatedResources := containerStatus.AllocatedResourcesStatus
|
||||||
|
for j, allocatedResource := range allocatedResources {
|
||||||
|
var container core.Container
|
||||||
|
containerFound := false
|
||||||
|
// get container by name
|
||||||
|
for _, c := range containers {
|
||||||
|
if c.Name == containerStatus.Name {
|
||||||
|
containerFound = true
|
||||||
|
container = c
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore missing container, see https://github.com/kubernetes/kubernetes/issues/124915
|
||||||
|
if containerFound {
|
||||||
|
found := false
|
||||||
|
|
||||||
|
// get container resources from the spec
|
||||||
|
containerResources := container.Resources
|
||||||
|
for resourceName := range containerResources.Requests {
|
||||||
|
if resourceName == allocatedResource.Name {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
allErrors = append(allErrors, field.Invalid(fldPath.Index(i).Child("allocatedResourcesStatus").Index(j).Child("name"), allocatedResource.Name, "must match one of the container's resource requirements"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uniqueResources := sets.New[core.ResourceID]()
|
||||||
|
// check resource IDs are unique
|
||||||
|
for k, r := range allocatedResource.Resources {
|
||||||
|
if r.Health != core.ResourceHealthStatusHealthy && r.Health != core.ResourceHealthStatusUnhealthy && r.Health != core.ResourceHealthStatusUnknown {
|
||||||
|
allErrors = append(allErrors, field.Invalid(fldPath.Index(i).Child("allocatedResourcesStatus").Index(j).Child("resources").Index(k).Child("health"), r.Health, "must be one of Healthy, Unhealthy, Unknown"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if uniqueResources.Has(r.ResourceID) {
|
||||||
|
allErrors = append(allErrors, field.Invalid(fldPath.Index(i).Child("allocatedResourcesStatus").Index(j).Child("resources").Index(k).Child("resourceID"), r.ResourceID, "must be unique"))
|
||||||
|
} else {
|
||||||
|
uniqueResources.Insert(r.ResourceID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allErrors
|
||||||
|
}
|
||||||
|
|
||||||
func validateLinuxContainerUser(linuxContainerUser *core.LinuxContainerUser, fldPath *field.Path) field.ErrorList {
|
func validateLinuxContainerUser(linuxContainerUser *core.LinuxContainerUser, fldPath *field.Path) field.ErrorList {
|
||||||
allErrors := field.ErrorList{}
|
allErrors := field.ErrorList{}
|
||||||
if linuxContainerUser == nil {
|
if linuxContainerUser == nil {
|
||||||
|
@ -24423,3 +24423,209 @@ func TestValidatePodStatusUpdateWithSupplementalGroupsPolicy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func TestValidateContainerStatusNoAllocatedResourcesStatus(t *testing.T) {
|
||||||
|
containerStatuses := []core.ContainerStatus{
|
||||||
|
{
|
||||||
|
Name: "container-1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "container-2",
|
||||||
|
AllocatedResourcesStatus: []core.ResourceStatus{
|
||||||
|
{
|
||||||
|
Name: "test.device/test",
|
||||||
|
Resources: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "container-3",
|
||||||
|
AllocatedResourcesStatus: []core.ResourceStatus{
|
||||||
|
{
|
||||||
|
Name: "test.device/test",
|
||||||
|
Resources: []core.ResourceHealth{
|
||||||
|
{
|
||||||
|
ResourceID: "resource-1",
|
||||||
|
Health: core.ResourceHealthStatusHealthy,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fldPath := field.NewPath("spec", "containers")
|
||||||
|
|
||||||
|
errs := validateContainerStatusNoAllocatedResourcesStatus(containerStatuses, fldPath)
|
||||||
|
|
||||||
|
assert.Equal(t, 2, len(errs))
|
||||||
|
assert.Equal(t, "spec.containers[1].allocatedResourcesStatus", errs[0].Field)
|
||||||
|
assert.Equal(t, "cannot be set for a container status", errs[0].Detail)
|
||||||
|
assert.Equal(t, "spec.containers[2].allocatedResourcesStatus", errs[1].Field)
|
||||||
|
assert.Equal(t, "cannot be set for a container status", errs[1].Detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateContainerStatusAllocatedResourcesStatus(t *testing.T) {
|
||||||
|
fldPath := field.NewPath("spec", "containers")
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
containers []core.Container
|
||||||
|
containerStatuses []core.ContainerStatus
|
||||||
|
wantFieldErrors field.ErrorList
|
||||||
|
}{
|
||||||
|
"basic correct status": {
|
||||||
|
containers: []core.Container{
|
||||||
|
{
|
||||||
|
Name: "container-1",
|
||||||
|
Resources: core.ResourceRequirements{
|
||||||
|
Requests: core.ResourceList{
|
||||||
|
"test.device/test": resource.MustParse("1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
containerStatuses: []core.ContainerStatus{
|
||||||
|
{
|
||||||
|
Name: "container-1",
|
||||||
|
AllocatedResourcesStatus: []core.ResourceStatus{
|
||||||
|
{
|
||||||
|
Name: "test.device/test",
|
||||||
|
Resources: []core.ResourceHealth{
|
||||||
|
{
|
||||||
|
ResourceID: "resource-1",
|
||||||
|
Health: core.ResourceHealthStatusHealthy,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantFieldErrors: field.ErrorList{},
|
||||||
|
},
|
||||||
|
"ignoring the missing container (see https://github.com/kubernetes/kubernetes/issues/124915)": {
|
||||||
|
containers: []core.Container{
|
||||||
|
{
|
||||||
|
Name: "container-2",
|
||||||
|
Resources: core.ResourceRequirements{
|
||||||
|
Requests: core.ResourceList{
|
||||||
|
"test.device/test": resource.MustParse("1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
containerStatuses: []core.ContainerStatus{
|
||||||
|
{
|
||||||
|
Name: "container-1",
|
||||||
|
AllocatedResourcesStatus: []core.ResourceStatus{
|
||||||
|
{
|
||||||
|
Name: "test.device/test",
|
||||||
|
Resources: []core.ResourceHealth{
|
||||||
|
{
|
||||||
|
ResourceID: "resource-1",
|
||||||
|
Health: core.ResourceHealthStatusHealthy,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantFieldErrors: field.ErrorList{},
|
||||||
|
},
|
||||||
|
"allow nil": {
|
||||||
|
containers: []core.Container{
|
||||||
|
{
|
||||||
|
Name: "container-2",
|
||||||
|
Resources: core.ResourceRequirements{
|
||||||
|
Requests: core.ResourceList{
|
||||||
|
"test.device/test": resource.MustParse("1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
containerStatuses: []core.ContainerStatus{
|
||||||
|
{
|
||||||
|
Name: "container-1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantFieldErrors: field.ErrorList{},
|
||||||
|
},
|
||||||
|
"don't allow non-unique IDs": {
|
||||||
|
containers: []core.Container{
|
||||||
|
{
|
||||||
|
Name: "container-2",
|
||||||
|
Resources: core.ResourceRequirements{
|
||||||
|
Requests: core.ResourceList{
|
||||||
|
"test.device/test": resource.MustParse("1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
containerStatuses: []core.ContainerStatus{
|
||||||
|
{
|
||||||
|
Name: "container-1",
|
||||||
|
AllocatedResourcesStatus: []core.ResourceStatus{
|
||||||
|
{
|
||||||
|
Name: "test.device/test",
|
||||||
|
Resources: []core.ResourceHealth{
|
||||||
|
{
|
||||||
|
ResourceID: "resource-1",
|
||||||
|
Health: core.ResourceHealthStatusHealthy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ResourceID: "resource-1",
|
||||||
|
Health: core.ResourceHealthStatusUnhealthy,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantFieldErrors: field.ErrorList{
|
||||||
|
field.Invalid(fldPath.Index(0).Child("allocatedResourcesStatus").Index(0).Child("resources").Index(1).Child("resourceID"), core.ResourceID("resource-1"), "must be unique"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"don't allow resources that are not in spec": {
|
||||||
|
containers: []core.Container{
|
||||||
|
{
|
||||||
|
Name: "container-1",
|
||||||
|
Resources: core.ResourceRequirements{
|
||||||
|
Requests: core.ResourceList{
|
||||||
|
"test.device/test": resource.MustParse("1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
containerStatuses: []core.ContainerStatus{
|
||||||
|
{
|
||||||
|
Name: "container-1",
|
||||||
|
AllocatedResourcesStatus: []core.ResourceStatus{
|
||||||
|
{
|
||||||
|
Name: "test.device/test",
|
||||||
|
Resources: []core.ResourceHealth{
|
||||||
|
{
|
||||||
|
ResourceID: "resource-1",
|
||||||
|
Health: core.ResourceHealthStatusHealthy,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "test.device/test2",
|
||||||
|
Resources: []core.ResourceHealth{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantFieldErrors: field.ErrorList{
|
||||||
|
field.Invalid(fldPath.Index(0).Child("allocatedResourcesStatus").Index(1).Child("name"), core.ResourceName("test.device/test2"), "must match one of the container's resource requirements"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, tt := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
errs := validateContainerStatusAllocatedResourcesStatus(tt.containerStatuses, fldPath, tt.containers)
|
||||||
|
if diff := cmp.Diff(tt.wantFieldErrors, errs); diff != "" {
|
||||||
|
t.Errorf("unexpected field errors (-want, +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user