diff --git a/pkg/api/pod/util.go b/pkg/api/pod/util.go index 54fc8a1c409..9ad259147d0 100644 --- a/pkg/api/pod/util.go +++ b/pkg/api/pod/util.go @@ -377,8 +377,6 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po // default pod validation options based on feature gate opts := apivalidation.PodValidationOptions{ AllowInvalidPodDeletionCost: !utilfeature.DefaultFeatureGate.Enabled(features.PodDeletionCost), - // Allow pod spec to use status.hostIPs in downward API if feature is enabled - AllowHostIPsField: utilfeature.DefaultFeatureGate.Enabled(features.PodHostIPs), // Do not allow pod spec to use non-integer multiple of huge page unit size default AllowIndivisibleHugePagesValues: false, AllowInvalidLabelValueInSelector: false, @@ -396,9 +394,6 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po opts.AllowOnlyRecursiveSELinuxChangePolicy = useOnlyRecursiveSELinuxChangePolicy(oldPodSpec) if oldPodSpec != nil { - // if old spec has status.hostIPs downwardAPI set, we must allow it - opts.AllowHostIPsField = opts.AllowHostIPsField || hasUsedDownwardAPIFieldPathWithPodSpec(oldPodSpec, "status.hostIPs") - // if old spec used non-integer multiple of huge page unit size, we must allow it opts.AllowIndivisibleHugePagesValues = usesIndivisibleHugePagesValues(oldPodSpec) @@ -536,55 +531,6 @@ func relaxedEnvVarUsed(name string, oldPodEnvVarNames sets.Set[string]) bool { return false } -func hasUsedDownwardAPIFieldPathWithPodSpec(podSpec *api.PodSpec, fieldPath string) bool { - if podSpec == nil { - return false - } - for _, vol := range podSpec.Volumes { - if hasUsedDownwardAPIFieldPathWithVolume(&vol, fieldPath) { - return true - } - } - for _, c := range podSpec.InitContainers { - if hasUsedDownwardAPIFieldPathWithContainer(&c, fieldPath) { - return true - } - } - for _, c := range podSpec.Containers { - if hasUsedDownwardAPIFieldPathWithContainer(&c, fieldPath) { - return true - } - } - return false -} - -func hasUsedDownwardAPIFieldPathWithVolume(volume *api.Volume, fieldPath string) bool { - if volume == nil || volume.DownwardAPI == nil { - return false - } - for _, file := range volume.DownwardAPI.Items { - if file.FieldRef != nil && - file.FieldRef.FieldPath == fieldPath { - return true - } - } - return false -} - -func hasUsedDownwardAPIFieldPathWithContainer(container *api.Container, fieldPath string) bool { - if container == nil { - return false - } - for _, env := range container.Env { - if env.ValueFrom != nil && - env.ValueFrom.FieldRef != nil && - env.ValueFrom.FieldRef.FieldPath == fieldPath { - return true - } - } - return false -} - // GetValidationOptionsFromPodTemplate will return pod validation options for specified template. func GetValidationOptionsFromPodTemplate(podTemplate, oldPodTemplate *api.PodTemplateSpec) apivalidation.PodValidationOptions { var newPodSpec, oldPodSpec *api.PodSpec @@ -859,11 +805,6 @@ func dropDisabledPodStatusFields(podStatus, oldPodStatus *api.PodStatus, podSpec podStatus.ResourceClaimStatuses = nil } - // drop HostIPs to empty (disable PodHostIPs). - if !utilfeature.DefaultFeatureGate.Enabled(features.PodHostIPs) && !hostIPsInUse(oldPodStatus) { - podStatus.HostIPs = nil - } - if !utilfeature.DefaultFeatureGate.Enabled(features.RecursiveReadOnlyMounts) && !rroInUse(oldPodSpec) { for i := range podStatus.ContainerStatuses { podStatus.ContainerStatuses[i].VolumeMounts = nil @@ -900,13 +841,6 @@ func dropDisabledPodStatusFields(podStatus, oldPodStatus *api.PodStatus, podSpec } } -func hostIPsInUse(podStatus *api.PodStatus) bool { - if podStatus == nil { - return false - } - return len(podStatus.HostIPs) > 0 -} - // dropDisabledDynamicResourceAllocationFields removes pod claim references from // container specs and pod-level resource claims unless they are already used // by the old pod spec. diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go index 5aa310e223d..e191948d3ff 100644 --- a/pkg/apis/core/validation/validation.go +++ b/pkg/apis/core/validation/validation.go @@ -1073,7 +1073,6 @@ func validateDownwardAPIVolumeFile(file *core.DownwardAPIVolumeFile, fldPath *fi if file.ResourceFieldRef != nil { allErrs = append(allErrs, field.Invalid(fldPath, "resource", "fieldRef and resourceFieldRef can not be specified simultaneously")) } - allErrs = append(allErrs, validateDownwardAPIHostIPs(file.FieldRef, fldPath.Child("fieldRef"), opts)...) } else if file.ResourceFieldRef != nil { localValidContainerResourceFieldPathPrefixes := validContainerResourceFieldPathPrefixesWithDownwardAPIHugePages allErrs = append(allErrs, validateContainerResourceFieldSelector(file.ResourceFieldRef, &validContainerResourceFieldPathExpressions, &localValidContainerResourceFieldPathPrefixes, fldPath.Child("resourceFieldRef"), true)...) @@ -2655,7 +2654,6 @@ func validateEnvVarValueFrom(ev core.EnvVar, fldPath *field.Path, opts PodValida if ev.ValueFrom.FieldRef != nil { numSources++ allErrs = append(allErrs, validateObjectFieldSelector(ev.ValueFrom.FieldRef, &validEnvDownwardAPIFieldPathExpressions, fldPath.Child("fieldRef"))...) - allErrs = append(allErrs, validateDownwardAPIHostIPs(ev.ValueFrom.FieldRef, fldPath.Child("fieldRef"), opts)...) } if ev.ValueFrom.ResourceFieldRef != nil { numSources++ @@ -2719,16 +2717,6 @@ func validateObjectFieldSelector(fs *core.ObjectFieldSelector, expressions *sets return allErrs } -func validateDownwardAPIHostIPs(fieldSel *core.ObjectFieldSelector, fldPath *field.Path, opts PodValidationOptions) field.ErrorList { - allErrs := field.ErrorList{} - if !opts.AllowHostIPsField { - if fieldSel.FieldPath == "status.hostIPs" { - allErrs = append(allErrs, field.Forbidden(fldPath, "may not be set when feature gate 'PodHostIPs' is not enabled")) - } - } - return allErrs -} - func validateContainerResourceFieldSelector(fs *core.ResourceFieldSelector, expressions *sets.Set[string], prefixes *sets.Set[string], fldPath *field.Path, volume bool) field.ErrorList { allErrs := field.ErrorList{} @@ -4041,8 +4029,6 @@ type PodValidationOptions struct { AllowInvalidLabelValueInSelector bool // Allow pod spec to use non-integer multiple of huge page unit size AllowIndivisibleHugePagesValues bool - // Allow pod spec to use status.hostIPs in downward API if feature is enabled - AllowHostIPsField bool // Allow invalid topologySpreadConstraint labelSelector for backward compatibility AllowInvalidTopologySpreadConstraintLabelSelector bool // Allow projected token volumes with non-local paths diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go index bd457b40adf..7a0fcaad1fb 100644 --- a/pkg/apis/core/validation/validation_test.go +++ b/pkg/apis/core/validation/validation_test.go @@ -23736,41 +23736,6 @@ func TestValidateAppArmorProfileFormat(t *testing.T) { } } -func TestValidateDownwardAPIHostIPs(t *testing.T) { - testCases := []struct { - name string - expectError bool - featureEnabled bool - fieldSel *core.ObjectFieldSelector - }{ - { - name: "has no hostIPs field, featuregate enabled", - expectError: false, - featureEnabled: true, - fieldSel: &core.ObjectFieldSelector{FieldPath: "status.hostIP"}, - }, - { - name: "has hostIPs field, featuregate enabled", - expectError: false, - featureEnabled: true, - fieldSel: &core.ObjectFieldSelector{FieldPath: "status.hostIPs"}, - }, - } - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodHostIPs, testCase.featureEnabled) - - errs := validateDownwardAPIHostIPs(testCase.fieldSel, field.NewPath("fieldSel"), PodValidationOptions{AllowHostIPsField: testCase.featureEnabled}) - if testCase.expectError && len(errs) == 0 { - t.Errorf("Unexpected success") - } - if !testCase.expectError && len(errs) != 0 { - t.Errorf("Unexpected error(s): %v", errs) - } - }) - } -} - func TestValidatePVSecretReference(t *testing.T) { rootFld := field.NewPath("name") type args struct { diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index a16463529de..f6d2165f7d0 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -472,12 +472,6 @@ const ( // sandbox creation and network configuration completes successfully PodReadyToStartContainersCondition featuregate.Feature = "PodReadyToStartContainersCondition" - // owner: @wzshiming - // kep: http://kep.k8s.io/2681 - // - // Adds pod.status.hostIPs and downward API - PodHostIPs featuregate.Feature = "PodHostIPs" - // owner: @AxeZhan // kep: http://kep.k8s.io/3960 // diff --git a/pkg/features/versioned_kube_features.go b/pkg/features/versioned_kube_features.go index 500e21b7676..80d2692eb27 100644 --- a/pkg/features/versioned_kube_features.go +++ b/pkg/features/versioned_kube_features.go @@ -558,12 +558,6 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate {Version: version.MustParse("1.31"), Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.33 }, - PodHostIPs: { - {Version: version.MustParse("1.28"), Default: false, PreRelease: featuregate.Alpha}, - {Version: version.MustParse("1.29"), Default: true, PreRelease: featuregate.Beta}, - {Version: version.MustParse("1.30"), Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.32 - }, - PodIndexLabel: { {Version: version.MustParse("1.28"), Default: true, PreRelease: featuregate.Beta}, {Version: version.MustParse("1.32"), Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.35 diff --git a/pkg/kubelet/kubelet_pods.go b/pkg/kubelet/kubelet_pods.go index e671c7af337..29fcce39917 100644 --- a/pkg/kubelet/kubelet_pods.go +++ b/pkg/kubelet/kubelet_pods.go @@ -949,9 +949,6 @@ func (kl *Kubelet) podFieldSelectorRuntimeValue(fs *v1.ObjectFieldSelector, pod } return hostIPs[0].String(), nil case "status.hostIPs": - if !utilfeature.DefaultFeatureGate.Enabled(features.PodHostIPs) { - return "", nil - } hostIPs, err := kl.getHostIPsAnyWay() if err != nil { return "", err @@ -1921,11 +1918,9 @@ func (kl *Kubelet) generateAPIPodStatus(pod *v1.Pod, podStatus *kubecontainer.Po } } s.HostIP = hostIPs[0].String() - if utilfeature.DefaultFeatureGate.Enabled(features.PodHostIPs) { - s.HostIPs = []v1.HostIP{{IP: s.HostIP}} - if len(hostIPs) == 2 { - s.HostIPs = append(s.HostIPs, v1.HostIP{IP: hostIPs[1].String()}) - } + s.HostIPs = []v1.HostIP{{IP: s.HostIP}} + if len(hostIPs) == 2 { + s.HostIPs = append(s.HostIPs, v1.HostIP{IP: hostIPs[1].String()}) } // HostNetwork Pods inherit the node IPs as PodIPs. They are immutable once set, diff --git a/test/conformance/testdata/conformance.yaml b/test/conformance/testdata/conformance.yaml index eac09a39db4..1c9978aada7 100755 --- a/test/conformance/testdata/conformance.yaml +++ b/test/conformance/testdata/conformance.yaml @@ -2152,6 +2152,14 @@ in the container. release: v1.9 file: test/e2e/common/node/downwardapi.go +- testname: DownwardAPI, environment for hostIPs + codename: '[sig-node] Downward API should provide hostIPs as an env var [NodeConformance] + [Conformance]' + description: Downward API MUST expose Pod and Container fields as environment variables. + Specify hostIPs as environment variable in the Pod Spec are visible at runtime + in the container. + release: v1.32 + file: test/e2e/common/node/downwardapi.go - testname: DownwardAPI, environment for Pod UID codename: '[sig-node] Downward API should provide pod UID as env vars [NodeConformance] [Conformance]' diff --git a/test/e2e/common/node/downwardapi.go b/test/e2e/common/node/downwardapi.go index a2f7c71a373..fc3ef6edf93 100644 --- a/test/e2e/common/node/downwardapi.go +++ b/test/e2e/common/node/downwardapi.go @@ -110,6 +110,32 @@ var _ = SIGDescribe("Downward API", func() { testDownwardAPI(ctx, f, podName, env, expectations) }) + /* + Release: v1.32 + Testname: DownwardAPI, environment for hostIPs + Description: Downward API MUST expose Pod and Container fields as environment variables. Specify hostIPs as environment variable in the Pod Spec are visible at runtime in the container. + */ + framework.ConformanceIt("should provide hostIPs as an env var", f.WithNodeConformance(), func(ctx context.Context) { + podName := "downward-api-" + string(uuid.NewUUID()) + env := []v1.EnvVar{ + { + Name: "HOST_IP", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{ + APIVersion: "v1", + FieldPath: "status.hostIP", + }, + }, + }, + } + + expectations := []string{ + fmt.Sprintf("HOST_IP=%v|%v", e2enetwork.RegexIPv4, e2enetwork.RegexIPv6), + } + + testDownwardAPI(ctx, f, podName, env, expectations) + }) + ginkgo.It("should provide host IP and pod IP as an env var if pod uses host network [LinuxOnly]", func(ctx context.Context) { podName := "downward-api-" + string(uuid.NewUUID()) env := []v1.EnvVar{ diff --git a/test/e2e_node/pod_host_ips.go b/test/e2e_node/pod_host_ips.go index 142b0f49f8d..2e6439608cb 100644 --- a/test/e2e_node/pod_host_ips.go +++ b/test/e2e_node/pod_host_ips.go @@ -24,20 +24,14 @@ import ( "github.com/onsi/gomega" v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/uuid" netutils "k8s.io/utils/net" - utilfeature "k8s.io/apiserver/pkg/util/feature" - kubefeatures "k8s.io/kubernetes/pkg/features" utilnode "k8s.io/kubernetes/pkg/util/node" "k8s.io/kubernetes/test/e2e/framework" - e2enetwork "k8s.io/kubernetes/test/e2e/framework/network" e2enode "k8s.io/kubernetes/test/e2e/framework/node" e2epod "k8s.io/kubernetes/test/e2e/framework/pod" - e2epodoutput "k8s.io/kubernetes/test/e2e/framework/pod/output" - e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" "k8s.io/kubernetes/test/e2e/network/common" imageutils "k8s.io/kubernetes/test/utils/image" admissionapi "k8s.io/pod-security-admission/api" @@ -109,31 +103,6 @@ var _ = common.SIGDescribe("Pod Host IPs", func() { err = podClient.Delete(ctx, pod.Name, *metav1.NewDeleteOptions(1)) framework.ExpectNoError(err, "failed to delete pod") }) - - ginkgo.It("should provide hostIPs as an env var", func(ctx context.Context) { - if !utilfeature.DefaultFeatureGate.Enabled(kubefeatures.PodHostIPs) { - e2eskipper.Skipf("PodHostIPs feature is not enabled") - } - - podName := "downward-api-" + string(uuid.NewUUID()) - env := []v1.EnvVar{ - { - Name: "HOST_IPS", - ValueFrom: &v1.EnvVarSource{ - FieldRef: &v1.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "status.hostIPs", - }, - }, - }, - } - - expectations := []string{ - fmt.Sprintf("HOST_IPS=%v|%v", e2enetwork.RegexIPv4, e2enetwork.RegexIPv6), - } - - testDownwardAPI(ctx, f, podName, env, expectations) - }) }) }) @@ -176,35 +145,3 @@ func genHostIPsForNode(ctx context.Context, f *framework.Framework, nodeName str } return nil, fmt.Errorf("no such node %q", nodeName) } - -func testDownwardAPI(ctx context.Context, f *framework.Framework, podName string, env []v1.EnvVar, expectations []string) { - pod := &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: podName, - Labels: map[string]string{"name": podName}, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Name: "dapi-container", - Image: imageutils.GetE2EImage(imageutils.BusyBox), - Command: []string{"sh", "-c", "env"}, - Resources: v1.ResourceRequirements{ - Requests: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("250m"), - v1.ResourceMemory: resource.MustParse("32Mi"), - }, - Limits: v1.ResourceList{ - v1.ResourceCPU: resource.MustParse("1250m"), - v1.ResourceMemory: resource.MustParse("64Mi"), - }, - }, - Env: env, - }, - }, - RestartPolicy: v1.RestartPolicyNever, - }, - } - - e2epodoutput.TestContainerOutputRegexp(ctx, f, "downward api env vars", pod, 0, expectations) -} diff --git a/test/featuregates_linter/test_data/versioned_feature_list.yaml b/test/featuregates_linter/test_data/versioned_feature_list.yaml index 9ccfebc5afc..7cac73eceef 100644 --- a/test/featuregates_linter/test_data/versioned_feature_list.yaml +++ b/test/featuregates_linter/test_data/versioned_feature_list.yaml @@ -872,20 +872,6 @@ lockToDefault: true preRelease: GA version: "1.31" -- name: PodHostIPs - versionedSpecs: - - default: false - lockToDefault: false - preRelease: Alpha - version: "1.28" - - default: true - lockToDefault: false - preRelease: Beta - version: "1.29" - - default: true - lockToDefault: true - preRelease: GA - version: "1.30" - name: PodIndexLabel versionedSpecs: - default: true