Merge pull request #124969 from RomanBednar/pv-phase-transition-time-ga

graduate PersistentVolumeLastPhaseTransitionTime to GA in 1.31
This commit is contained in:
Kubernetes Prow Robot 2024-07-11 15:29:19 -07:00 committed by GitHub
commit 1080554307
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 27 additions and 204 deletions

View File

@ -9052,7 +9052,7 @@
"properties": {
"lastPhaseTransitionTime": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time",
"description": "lastPhaseTransitionTime is the time the phase transitioned from one to another and automatically resets to current time everytime a volume phase transitions. This is a beta field and requires the PersistentVolumeLastPhaseTransitionTime feature to be enabled (enabled by default)."
"description": "lastPhaseTransitionTime is the time the phase transitioned from one to another and automatically resets to current time everytime a volume phase transitions."
},
"message": {
"description": "message is a human-readable message indicating details about why the volume is in this state.",

View File

@ -4863,7 +4863,7 @@
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time"
}
],
"description": "lastPhaseTransitionTime is the time the phase transitioned from one to another and automatically resets to current time everytime a volume phase transitions. This is a beta field and requires the PersistentVolumeLastPhaseTransitionTime feature to be enabled (enabled by default)."
"description": "lastPhaseTransitionTime is the time the phase transitioned from one to another and automatically resets to current time everytime a volume phase transitions."
},
"message": {
"description": "message is a human-readable message indicating details about why the volume is in this state.",

View File

@ -41,14 +41,6 @@ func DropDisabledSpecFields(pvSpec *api.PersistentVolumeSpec, oldPVSpec *api.Per
}
}
// DropDisabledStatusFields removes disabled fields from the pv status.
// This should be called from PrepareForUpdate for all resources containing a pv status.
func DropDisabledStatusFields(oldStatus, newStatus *api.PersistentVolumeStatus) {
if !utilfeature.DefaultFeatureGate.Enabled(features.PersistentVolumeLastPhaseTransitionTime) && oldStatus.LastPhaseTransitionTime.IsZero() {
newStatus.LastPhaseTransitionTime = nil
}
}
func GetWarningsForPersistentVolume(pv *api.PersistentVolume) []string {
if pv == nil {
return nil

View File

@ -392,8 +392,6 @@ type PersistentVolumeStatus struct {
Reason string
// LastPhaseTransitionTime is the time the phase transitioned from one to another
// and automatically resets to current time everytime a volume phase transitions.
// This is a beta field and requires the PersistentVolumeLastPhaseTransitionTime feature to be enabled (enabled by default).
// +featureGate=PersistentVolumeLastPhaseTransitionTime
// +optional
LastPhaseTransitionTime *metav1.Time
}

View File

@ -544,6 +544,7 @@ const (
// kep: https://kep.k8s.io/3762
// alpha: v1.28
// beta: v1.29
// GA: v1.31
//
// Adds a new field to persistent volumes which holds a timestamp of when the volume last transitioned its phase.
PersistentVolumeLastPhaseTransitionTime featuregate.Feature = "PersistentVolumeLastPhaseTransitionTime"
@ -1112,7 +1113,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
PDBUnhealthyPodEvictionPolicy: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.33
PersistentVolumeLastPhaseTransitionTime: {Default: true, PreRelease: featuregate.Beta},
PersistentVolumeLastPhaseTransitionTime: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.33
PodAndContainerStatsFromCRI: {Default: false, PreRelease: featuregate.Alpha},

View File

@ -26138,7 +26138,7 @@ func schema_k8sio_api_core_v1_PersistentVolumeStatus(ref common.ReferenceCallbac
},
"lastPhaseTransitionTime": {
SchemaProps: spec.SchemaProps{
Description: "lastPhaseTransitionTime is the time the phase transitioned from one to another and automatically resets to current time everytime a volume phase transitions. This is a beta field and requires the PersistentVolumeLastPhaseTransitionTime feature to be enabled (enabled by default).",
Description: "lastPhaseTransitionTime is the time the phase transitioned from one to another and automatically resets to current time everytime a volume phase transitions.",
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"),
},
},

View File

@ -33,10 +33,7 @@ import (
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
"k8s.io/apiserver/pkg/registry/rest"
etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/registry/core/persistentvolume"
"k8s.io/kubernetes/pkg/registry/registrytest"
)
@ -174,7 +171,6 @@ func TestWatch(t *testing.T) {
}
func TestUpdateStatus(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentVolumeLastPhaseTransitionTime, true)
storage, statusStorage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()

View File

@ -21,9 +21,6 @@ import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/features"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
@ -70,11 +67,9 @@ func (persistentvolumeStrategy) PrepareForCreate(ctx context.Context, obj runtim
pv := obj.(*api.PersistentVolume)
pv.Status = api.PersistentVolumeStatus{}
if utilfeature.DefaultFeatureGate.Enabled(features.PersistentVolumeLastPhaseTransitionTime) {
pv.Status.Phase = api.VolumePending
now := NowFunc()
pv.Status.LastPhaseTransitionTime = &now
}
pv.Status.Phase = api.VolumePending
now := NowFunc()
pv.Status.LastPhaseTransitionTime = &now
}
func (persistentvolumeStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
@ -148,20 +143,17 @@ func (persistentvolumeStatusStrategy) PrepareForUpdate(ctx context.Context, obj,
oldPv := old.(*api.PersistentVolume)
newPv.Spec = oldPv.Spec
if utilfeature.DefaultFeatureGate.Enabled(features.PersistentVolumeLastPhaseTransitionTime) {
switch {
case oldPv.Status.Phase == newPv.Status.Phase && newPv.Status.LastPhaseTransitionTime == nil:
// phase didn't change, preserve the existing transition time if set
newPv.Status.LastPhaseTransitionTime = oldPv.Status.LastPhaseTransitionTime
switch {
case oldPv.Status.Phase == newPv.Status.Phase && newPv.Status.LastPhaseTransitionTime == nil:
// phase didn't change, preserve the existing transition time if set
newPv.Status.LastPhaseTransitionTime = oldPv.Status.LastPhaseTransitionTime
case oldPv.Status.Phase != newPv.Status.Phase && (newPv.Status.LastPhaseTransitionTime == nil || newPv.Status.LastPhaseTransitionTime.Equal(oldPv.Status.LastPhaseTransitionTime)):
// phase changed and client didn't set or didn't change the transition time
now := NowFunc()
newPv.Status.LastPhaseTransitionTime = &now
}
case oldPv.Status.Phase != newPv.Status.Phase && (newPv.Status.LastPhaseTransitionTime == nil || newPv.Status.LastPhaseTransitionTime.Equal(oldPv.Status.LastPhaseTransitionTime)):
// phase changed and client didn't set or didn't change the transition time
now := NowFunc()
newPv.Status.LastPhaseTransitionTime = &now
}
pvutil.DropDisabledStatusFields(&oldPv.Status, &newPv.Status)
}
func (persistentvolumeStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {

View File

@ -21,11 +21,8 @@ import (
"github.com/google/go-cmp/cmp"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
apitesting "k8s.io/kubernetes/pkg/api/testing"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
"reflect"
"testing"
"time"
@ -53,14 +50,12 @@ func TestStatusUpdate(t *testing.T) {
}()
tests := []struct {
name string
fg bool
oldObj *api.PersistentVolume
newObj *api.PersistentVolume
expectedObj *api.PersistentVolume
}{
{
name: "feature enabled: timestamp is updated when phase changes",
fg: true,
name: "timestamp is updated when phase changes",
oldObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@ -88,8 +83,7 @@ func TestStatusUpdate(t *testing.T) {
},
},
{
name: "feature enabled: timestamp is updated when phase changes and old pv has a timestamp",
fg: true,
name: "timestamp is updated when phase changes and old pv has a timestamp",
oldObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@ -118,8 +112,7 @@ func TestStatusUpdate(t *testing.T) {
},
},
{
name: "feature enabled: user timestamp change is respected on no phase change",
fg: true,
name: "user timestamp change is respected on no phase change",
oldObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@ -148,8 +141,7 @@ func TestStatusUpdate(t *testing.T) {
},
},
{
name: "feature enabled: user timestamp is respected on phase change",
fg: true,
name: "user timestamp is respected on phase change",
oldObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@ -179,8 +171,7 @@ func TestStatusUpdate(t *testing.T) {
},
},
{
name: "feature enabled: user timestamp change is respected on no phase change when old pv has a timestamp",
fg: true,
name: "user timestamp change is respected on no phase change when old pv has a timestamp",
oldObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@ -210,8 +201,7 @@ func TestStatusUpdate(t *testing.T) {
},
},
{
name: "feature enabled: timestamp is updated when phase changes and both new and old timestamp matches",
fg: true,
name: "timestamp is updated when phase changes and both new and old timestamp matches",
oldObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@ -240,131 +230,10 @@ func TestStatusUpdate(t *testing.T) {
},
},
},
{
name: "feature disabled: timestamp is not updated",
fg: false,
oldObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Status: api.PersistentVolumeStatus{
Phase: api.VolumePending,
},
},
newObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Status: api.PersistentVolumeStatus{
Phase: api.VolumeBound,
},
},
expectedObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Status: api.PersistentVolumeStatus{
Phase: api.VolumeBound,
},
},
},
{
name: "feature disabled: user timestamp is overwritten on phase change to nil",
fg: false,
oldObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Status: api.PersistentVolumeStatus{
Phase: api.VolumePending,
},
},
newObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Status: api.PersistentVolumeStatus{
Phase: api.VolumePending,
LastPhaseTransitionTime: &later,
},
},
expectedObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Status: api.PersistentVolumeStatus{
Phase: api.VolumePending,
LastPhaseTransitionTime: nil,
},
},
},
{
name: "feature disabled: user timestamp change is respected on phase change",
fg: false,
oldObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Status: api.PersistentVolumeStatus{
Phase: api.VolumePending,
LastPhaseTransitionTime: &origin,
},
},
newObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Status: api.PersistentVolumeStatus{
Phase: api.VolumeBound,
LastPhaseTransitionTime: &later,
},
},
expectedObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Status: api.PersistentVolumeStatus{
Phase: api.VolumeBound,
LastPhaseTransitionTime: &later,
},
},
},
{
name: "feature disabled: user timestamp change is respected on no phase change",
fg: false,
oldObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Status: api.PersistentVolumeStatus{
Phase: api.VolumeBound,
LastPhaseTransitionTime: &origin,
},
},
newObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Status: api.PersistentVolumeStatus{
Phase: api.VolumeBound,
LastPhaseTransitionTime: &later,
},
},
expectedObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Status: api.PersistentVolumeStatus{
Phase: api.VolumeBound,
LastPhaseTransitionTime: &later,
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentVolumeLastPhaseTransitionTime, tc.fg)
obj := tc.newObj.DeepCopy()
StatusStrategy.PrepareForUpdate(context.TODO(), obj, tc.oldObj.DeepCopy())
@ -383,13 +252,11 @@ func TestStatusCreate(t *testing.T) {
}()
tests := []struct {
name string
fg bool
newObj *api.PersistentVolume
expectedObj *api.PersistentVolume
}{
{
name: "feature enabled: pv is in pending phase and has a timestamp",
fg: true,
name: "pv is in pending phase and has a timestamp",
newObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@ -405,26 +272,11 @@ func TestStatusCreate(t *testing.T) {
},
},
},
{
name: "feature disabled: pv does not have phase and timestamp",
fg: false,
newObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
},
expectedObj: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentVolumeLastPhaseTransitionTime, tc.fg)
obj := tc.newObj.DeepCopy()
StatusStrategy.PrepareForCreate(context.TODO(), obj)
if !reflect.DeepEqual(obj, tc.expectedObj) {

View File

@ -3415,8 +3415,6 @@ message PersistentVolumeStatus {
// lastPhaseTransitionTime is the time the phase transitioned from one to another
// and automatically resets to current time everytime a volume phase transitions.
// This is a beta field and requires the PersistentVolumeLastPhaseTransitionTime feature to be enabled (enabled by default).
// +featureGate=PersistentVolumeLastPhaseTransitionTime
// +optional
optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time lastPhaseTransitionTime = 4;
}

View File

@ -426,8 +426,6 @@ type PersistentVolumeStatus struct {
Reason string `json:"reason,omitempty" protobuf:"bytes,3,opt,name=reason"`
// lastPhaseTransitionTime is the time the phase transitioned from one to another
// and automatically resets to current time everytime a volume phase transitions.
// This is a beta field and requires the PersistentVolumeLastPhaseTransitionTime feature to be enabled (enabled by default).
// +featureGate=PersistentVolumeLastPhaseTransitionTime
// +optional
LastPhaseTransitionTime *metav1.Time `json:"lastPhaseTransitionTime,omitempty" protobuf:"bytes,4,opt,name=lastPhaseTransitionTime"`
}

View File

@ -1511,7 +1511,7 @@ var map_PersistentVolumeStatus = map[string]string{
"phase": "phase indicates if a volume is available, bound to a claim, or released by a claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#phase",
"message": "message is a human-readable message indicating details about why the volume is in this state.",
"reason": "reason is a brief CamelCase string that describes any failure and is meant for machine parsing and tidy display in the CLI.",
"lastPhaseTransitionTime": "lastPhaseTransitionTime is the time the phase transitioned from one to another and automatically resets to current time everytime a volume phase transitions. This is a beta field and requires the PersistentVolumeLastPhaseTransitionTime feature to be enabled (enabled by default).",
"lastPhaseTransitionTime": "lastPhaseTransitionTime is the time the phase transitioned from one to another and automatically resets to current time everytime a volume phase transitions.",
}
func (PersistentVolumeStatus) SwaggerDoc() map[string]string {

View File

@ -237,9 +237,6 @@ var (
// Marks a single test that tests pod-to-pod connectivity between every pair of nodes.
NoSNAT = framework.WithFeature(framework.ValidFeatures.Add("NoSNAT"))
// TODO: document the feature (owning SIG, when to use this feature for a test)
PersistentVolumeLastPhaseTransitionTime = framework.WithFeature(framework.ValidFeatures.Add("PersistentVolumeLastPhaseTransitionTime"))
// Owner: sig-network
// Marks a single test that tests cluster DNS performance with many services.
PerformanceDNS = framework.WithFeature(framework.ValidFeatures.Add("PerformanceDNS"))

View File

@ -34,7 +34,6 @@ import (
"k8s.io/apimachinery/pkg/util/uuid"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/util/retry"
"k8s.io/kubernetes/test/e2e/feature"
"k8s.io/kubernetes/test/e2e/framework"
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
e2epv "k8s.io/kubernetes/test/e2e/framework/pv"
@ -213,7 +212,7 @@ var _ = utils.SIGDescribe("PersistentVolumes", func() {
})
// Create new PV without claim, verify it's in Available state and LastPhaseTransitionTime is set.
f.It("create a PV: test phase transition timestamp is set and phase is Available", feature.PersistentVolumeLastPhaseTransitionTime, func(ctx context.Context) {
f.It("create a PV: test phase transition timestamp is set and phase is Available", func(ctx context.Context) {
pvObj := e2epv.MakePersistentVolume(pvConfig)
pv, err = e2epv.CreatePV(ctx, c, f.Timeouts, pvObj)
framework.ExpectNoError(err)
@ -232,7 +231,7 @@ var _ = utils.SIGDescribe("PersistentVolumes", func() {
// Create PV and pre-bound PVC that matches the PV, verify that when PV and PVC bind
// the LastPhaseTransitionTime filed of the PV is updated.
f.It("create a PV and a pre-bound PVC: test phase transition timestamp is set", feature.PersistentVolumeLastPhaseTransitionTime, func(ctx context.Context) {
f.It("create a PV and a pre-bound PVC: test phase transition timestamp is set", func(ctx context.Context) {
pv, pvc, err = e2epv.CreatePVPVC(ctx, c, f.Timeouts, pvConfig, pvcConfig, ns, true)
framework.ExpectNoError(err)
@ -252,7 +251,7 @@ var _ = utils.SIGDescribe("PersistentVolumes", func() {
// Create PV and pre-bound PVC that matches the PV, verify that when PV and PVC bind
// the LastPhaseTransitionTime field of the PV is set, then delete the PVC to change PV phase to
// released and validate PV LastPhaseTransitionTime correctly updated timestamp.
f.It("create a PV and a pre-bound PVC: test phase transition timestamp multiple updates", feature.PersistentVolumeLastPhaseTransitionTime, func(ctx context.Context) {
f.It("create a PV and a pre-bound PVC: test phase transition timestamp multiple updates", func(ctx context.Context) {
pv, pvc, err = e2epv.CreatePVPVC(ctx, c, f.Timeouts, pvConfig, pvcConfig, ns, true)
framework.ExpectNoError(err)