graduate PersistentVolumeLastPhaseTransitionTime to GA in 1.31
This commit is contained in:
		
							
								
								
									
										2
									
								
								api/openapi-spec/swagger.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								api/openapi-spec/swagger.json
									
									
									
										generated
									
									
									
								
							@@ -9023,7 +9023,7 @@
 | 
				
			|||||||
      "properties": {
 | 
					      "properties": {
 | 
				
			||||||
        "lastPhaseTransitionTime": {
 | 
					        "lastPhaseTransitionTime": {
 | 
				
			||||||
          "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time",
 | 
					          "$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": {
 | 
					        "message": {
 | 
				
			||||||
          "description": "message is a human-readable message indicating details about why the volume is in this state.",
 | 
					          "description": "message is a human-readable message indicating details about why the volume is in this state.",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4817,7 +4817,7 @@
 | 
				
			|||||||
                "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time"
 | 
					                "$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": {
 | 
					          "message": {
 | 
				
			||||||
            "description": "message is a human-readable message indicating details about why the volume is in this state.",
 | 
					            "description": "message is a human-readable message indicating details about why the volume is in this state.",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 {
 | 
					func GetWarningsForPersistentVolume(pv *api.PersistentVolume) []string {
 | 
				
			||||||
	if pv == nil {
 | 
						if pv == nil {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -392,8 +392,6 @@ type PersistentVolumeStatus struct {
 | 
				
			|||||||
	Reason string
 | 
						Reason string
 | 
				
			||||||
	// LastPhaseTransitionTime is the time the phase transitioned from one to another
 | 
						// LastPhaseTransitionTime is the time the phase transitioned from one to another
 | 
				
			||||||
	// and automatically resets to current time everytime a volume phase transitions.
 | 
						// 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
 | 
				
			||||||
	LastPhaseTransitionTime *metav1.Time
 | 
						LastPhaseTransitionTime *metav1.Time
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -534,6 +534,7 @@ const (
 | 
				
			|||||||
	// kep: https://kep.k8s.io/3762
 | 
						// kep: https://kep.k8s.io/3762
 | 
				
			||||||
	// alpha: v1.28
 | 
						// alpha: v1.28
 | 
				
			||||||
	// beta: v1.29
 | 
						// 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.
 | 
						// Adds a new field to persistent volumes which holds a timestamp of when the volume last transitioned its phase.
 | 
				
			||||||
	PersistentVolumeLastPhaseTransitionTime featuregate.Feature = "PersistentVolumeLastPhaseTransitionTime"
 | 
						PersistentVolumeLastPhaseTransitionTime featuregate.Feature = "PersistentVolumeLastPhaseTransitionTime"
 | 
				
			||||||
@@ -1089,7 +1090,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	PDBUnhealthyPodEvictionPolicy: {Default: true, PreRelease: featuregate.Beta},
 | 
						PDBUnhealthyPodEvictionPolicy: {Default: true, PreRelease: featuregate.Beta},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	PersistentVolumeLastPhaseTransitionTime: {Default: true, PreRelease: featuregate.Beta},
 | 
						PersistentVolumeLastPhaseTransitionTime: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.33
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	PodAndContainerStatsFromCRI: {Default: false, PreRelease: featuregate.Alpha},
 | 
						PodAndContainerStatsFromCRI: {Default: false, PreRelease: featuregate.Alpha},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								pkg/generated/openapi/zz_generated.openapi.go
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								pkg/generated/openapi/zz_generated.openapi.go
									
									
									
										generated
									
									
									
								
							@@ -26072,7 +26072,7 @@ func schema_k8sio_api_core_v1_PersistentVolumeStatus(ref common.ReferenceCallbac
 | 
				
			|||||||
					},
 | 
										},
 | 
				
			||||||
					"lastPhaseTransitionTime": {
 | 
										"lastPhaseTransitionTime": {
 | 
				
			||||||
						SchemaProps: spec.SchemaProps{
 | 
											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"),
 | 
												Ref:         ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"),
 | 
				
			||||||
						},
 | 
											},
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,10 +33,7 @@ import (
 | 
				
			|||||||
	genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
 | 
						genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
 | 
				
			||||||
	"k8s.io/apiserver/pkg/registry/rest"
 | 
						"k8s.io/apiserver/pkg/registry/rest"
 | 
				
			||||||
	etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
 | 
						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"
 | 
						api "k8s.io/kubernetes/pkg/apis/core"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/features"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/pkg/registry/core/persistentvolume"
 | 
						"k8s.io/kubernetes/pkg/registry/core/persistentvolume"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/registry/registrytest"
 | 
						"k8s.io/kubernetes/pkg/registry/registrytest"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -174,7 +171,6 @@ func TestWatch(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestUpdateStatus(t *testing.T) {
 | 
					func TestUpdateStatus(t *testing.T) {
 | 
				
			||||||
	featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentVolumeLastPhaseTransitionTime, true)
 | 
					 | 
				
			||||||
	storage, statusStorage, server := newStorage(t)
 | 
						storage, statusStorage, server := newStorage(t)
 | 
				
			||||||
	defer server.Terminate(t)
 | 
						defer server.Terminate(t)
 | 
				
			||||||
	defer storage.Store.DestroyFunc()
 | 
						defer storage.Store.DestroyFunc()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,9 +21,6 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						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/fields"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/labels"
 | 
						"k8s.io/apimachinery/pkg/labels"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
						"k8s.io/apimachinery/pkg/runtime"
 | 
				
			||||||
@@ -70,12 +67,10 @@ func (persistentvolumeStrategy) PrepareForCreate(ctx context.Context, obj runtim
 | 
				
			|||||||
	pv := obj.(*api.PersistentVolume)
 | 
						pv := obj.(*api.PersistentVolume)
 | 
				
			||||||
	pv.Status = api.PersistentVolumeStatus{}
 | 
						pv.Status = api.PersistentVolumeStatus{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if utilfeature.DefaultFeatureGate.Enabled(features.PersistentVolumeLastPhaseTransitionTime) {
 | 
					 | 
				
			||||||
	pv.Status.Phase = api.VolumePending
 | 
						pv.Status.Phase = api.VolumePending
 | 
				
			||||||
	now := NowFunc()
 | 
						now := NowFunc()
 | 
				
			||||||
	pv.Status.LastPhaseTransitionTime = &now
 | 
						pv.Status.LastPhaseTransitionTime = &now
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (persistentvolumeStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
 | 
					func (persistentvolumeStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
 | 
				
			||||||
	persistentvolume := obj.(*api.PersistentVolume)
 | 
						persistentvolume := obj.(*api.PersistentVolume)
 | 
				
			||||||
@@ -148,7 +143,6 @@ func (persistentvolumeStatusStrategy) PrepareForUpdate(ctx context.Context, obj,
 | 
				
			|||||||
	oldPv := old.(*api.PersistentVolume)
 | 
						oldPv := old.(*api.PersistentVolume)
 | 
				
			||||||
	newPv.Spec = oldPv.Spec
 | 
						newPv.Spec = oldPv.Spec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if utilfeature.DefaultFeatureGate.Enabled(features.PersistentVolumeLastPhaseTransitionTime) {
 | 
					 | 
				
			||||||
	switch {
 | 
						switch {
 | 
				
			||||||
	case oldPv.Status.Phase == newPv.Status.Phase && newPv.Status.LastPhaseTransitionTime == nil:
 | 
						case oldPv.Status.Phase == newPv.Status.Phase && newPv.Status.LastPhaseTransitionTime == nil:
 | 
				
			||||||
		// phase didn't change, preserve the existing transition time if set
 | 
							// phase didn't change, preserve the existing transition time if set
 | 
				
			||||||
@@ -159,9 +153,7 @@ func (persistentvolumeStatusStrategy) PrepareForUpdate(ctx context.Context, obj,
 | 
				
			|||||||
		now := NowFunc()
 | 
							now := NowFunc()
 | 
				
			||||||
		newPv.Status.LastPhaseTransitionTime = &now
 | 
							newPv.Status.LastPhaseTransitionTime = &now
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pvutil.DropDisabledStatusFields(&oldPv.Status, &newPv.Status)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (persistentvolumeStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
 | 
					func (persistentvolumeStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,11 +21,8 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"github.com/google/go-cmp/cmp"
 | 
						"github.com/google/go-cmp/cmp"
 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						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"
 | 
						apitesting "k8s.io/kubernetes/pkg/api/testing"
 | 
				
			||||||
	api "k8s.io/kubernetes/pkg/apis/core"
 | 
						api "k8s.io/kubernetes/pkg/apis/core"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/features"
 | 
					 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
@@ -53,14 +50,12 @@ func TestStatusUpdate(t *testing.T) {
 | 
				
			|||||||
	}()
 | 
						}()
 | 
				
			||||||
	tests := []struct {
 | 
						tests := []struct {
 | 
				
			||||||
		name        string
 | 
							name        string
 | 
				
			||||||
		fg          bool
 | 
					 | 
				
			||||||
		oldObj      *api.PersistentVolume
 | 
							oldObj      *api.PersistentVolume
 | 
				
			||||||
		newObj      *api.PersistentVolume
 | 
							newObj      *api.PersistentVolume
 | 
				
			||||||
		expectedObj *api.PersistentVolume
 | 
							expectedObj *api.PersistentVolume
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "feature enabled: timestamp is updated when phase changes",
 | 
								name: "timestamp is updated when phase changes",
 | 
				
			||||||
			fg:   true,
 | 
					 | 
				
			||||||
			oldObj: &api.PersistentVolume{
 | 
								oldObj: &api.PersistentVolume{
 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
									ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
					Name: "foo",
 | 
										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",
 | 
								name: "timestamp is updated when phase changes and old pv has a timestamp",
 | 
				
			||||||
			fg:   true,
 | 
					 | 
				
			||||||
			oldObj: &api.PersistentVolume{
 | 
								oldObj: &api.PersistentVolume{
 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
									ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
					Name: "foo",
 | 
										Name: "foo",
 | 
				
			||||||
@@ -118,8 +112,7 @@ func TestStatusUpdate(t *testing.T) {
 | 
				
			|||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "feature enabled: user timestamp change is respected on no phase change",
 | 
								name: "user timestamp change is respected on no phase change",
 | 
				
			||||||
			fg:   true,
 | 
					 | 
				
			||||||
			oldObj: &api.PersistentVolume{
 | 
								oldObj: &api.PersistentVolume{
 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
									ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
					Name: "foo",
 | 
										Name: "foo",
 | 
				
			||||||
@@ -148,8 +141,7 @@ func TestStatusUpdate(t *testing.T) {
 | 
				
			|||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "feature enabled: user timestamp is respected on phase change",
 | 
								name: "user timestamp is respected on phase change",
 | 
				
			||||||
			fg:   true,
 | 
					 | 
				
			||||||
			oldObj: &api.PersistentVolume{
 | 
								oldObj: &api.PersistentVolume{
 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
									ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
					Name: "foo",
 | 
										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",
 | 
								name: "user timestamp change is respected on no phase change when old pv has a timestamp",
 | 
				
			||||||
			fg:   true,
 | 
					 | 
				
			||||||
			oldObj: &api.PersistentVolume{
 | 
								oldObj: &api.PersistentVolume{
 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
									ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
					Name: "foo",
 | 
										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",
 | 
								name: "timestamp is updated when phase changes and both new and old timestamp matches",
 | 
				
			||||||
			fg:   true,
 | 
					 | 
				
			||||||
			oldObj: &api.PersistentVolume{
 | 
								oldObj: &api.PersistentVolume{
 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
									ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
					Name: "foo",
 | 
										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 {
 | 
						for _, tc := range tests {
 | 
				
			||||||
		t.Run(tc.name, func(t *testing.T) {
 | 
							t.Run(tc.name, func(t *testing.T) {
 | 
				
			||||||
			featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentVolumeLastPhaseTransitionTime, tc.fg)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			obj := tc.newObj.DeepCopy()
 | 
								obj := tc.newObj.DeepCopy()
 | 
				
			||||||
			StatusStrategy.PrepareForUpdate(context.TODO(), obj, tc.oldObj.DeepCopy())
 | 
								StatusStrategy.PrepareForUpdate(context.TODO(), obj, tc.oldObj.DeepCopy())
 | 
				
			||||||
@@ -383,13 +252,11 @@ func TestStatusCreate(t *testing.T) {
 | 
				
			|||||||
	}()
 | 
						}()
 | 
				
			||||||
	tests := []struct {
 | 
						tests := []struct {
 | 
				
			||||||
		name        string
 | 
							name        string
 | 
				
			||||||
		fg          bool
 | 
					 | 
				
			||||||
		newObj      *api.PersistentVolume
 | 
							newObj      *api.PersistentVolume
 | 
				
			||||||
		expectedObj *api.PersistentVolume
 | 
							expectedObj *api.PersistentVolume
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "feature enabled: pv is in pending phase and has a timestamp",
 | 
								name: "pv is in pending phase and has a timestamp",
 | 
				
			||||||
			fg:   true,
 | 
					 | 
				
			||||||
			newObj: &api.PersistentVolume{
 | 
								newObj: &api.PersistentVolume{
 | 
				
			||||||
				ObjectMeta: metav1.ObjectMeta{
 | 
									ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
					Name: "foo",
 | 
										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 {
 | 
						for _, tc := range tests {
 | 
				
			||||||
		t.Run(tc.name, func(t *testing.T) {
 | 
							t.Run(tc.name, func(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentVolumeLastPhaseTransitionTime, tc.fg)
 | 
					 | 
				
			||||||
			obj := tc.newObj.DeepCopy()
 | 
								obj := tc.newObj.DeepCopy()
 | 
				
			||||||
			StatusStrategy.PrepareForCreate(context.TODO(), obj)
 | 
								StatusStrategy.PrepareForCreate(context.TODO(), obj)
 | 
				
			||||||
			if !reflect.DeepEqual(obj, tc.expectedObj) {
 | 
								if !reflect.DeepEqual(obj, tc.expectedObj) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3406,8 +3406,6 @@ message PersistentVolumeStatus {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // lastPhaseTransitionTime is the time the phase transitioned from one to another
 | 
					  // lastPhaseTransitionTime is the time the phase transitioned from one to another
 | 
				
			||||||
  // and automatically resets to current time everytime a volume phase transitions.
 | 
					  // 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
 | 
				
			||||||
  optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time lastPhaseTransitionTime = 4;
 | 
					  optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time lastPhaseTransitionTime = 4;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -425,8 +425,6 @@ type PersistentVolumeStatus struct {
 | 
				
			|||||||
	Reason string `json:"reason,omitempty" protobuf:"bytes,3,opt,name=reason"`
 | 
						Reason string `json:"reason,omitempty" protobuf:"bytes,3,opt,name=reason"`
 | 
				
			||||||
	// lastPhaseTransitionTime is the time the phase transitioned from one to another
 | 
						// lastPhaseTransitionTime is the time the phase transitioned from one to another
 | 
				
			||||||
	// and automatically resets to current time everytime a volume phase transitions.
 | 
						// 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
 | 
				
			||||||
	LastPhaseTransitionTime *metav1.Time `json:"lastPhaseTransitionTime,omitempty" protobuf:"bytes,4,opt,name=lastPhaseTransitionTime"`
 | 
						LastPhaseTransitionTime *metav1.Time `json:"lastPhaseTransitionTime,omitempty" protobuf:"bytes,4,opt,name=lastPhaseTransitionTime"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1500,7 +1500,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",
 | 
						"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.",
 | 
						"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.",
 | 
						"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 {
 | 
					func (PersistentVolumeStatus) SwaggerDoc() map[string]string {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -243,9 +243,6 @@ var (
 | 
				
			|||||||
	// Marks a single test that tests pod-to-pod connectivity between every pair of nodes.
 | 
						// Marks a single test that tests pod-to-pod connectivity between every pair of nodes.
 | 
				
			||||||
	NoSNAT = framework.WithFeature(framework.ValidFeatures.Add("NoSNAT"))
 | 
						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
 | 
						// Owner: sig-network
 | 
				
			||||||
	// Marks a single test that tests cluster DNS performance with many services.
 | 
						// Marks a single test that tests cluster DNS performance with many services.
 | 
				
			||||||
	PerformanceDNS = framework.WithFeature(framework.ValidFeatures.Add("PerformanceDNS"))
 | 
						PerformanceDNS = framework.WithFeature(framework.ValidFeatures.Add("PerformanceDNS"))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,7 +34,6 @@ import (
 | 
				
			|||||||
	"k8s.io/apimachinery/pkg/util/uuid"
 | 
						"k8s.io/apimachinery/pkg/util/uuid"
 | 
				
			||||||
	clientset "k8s.io/client-go/kubernetes"
 | 
						clientset "k8s.io/client-go/kubernetes"
 | 
				
			||||||
	"k8s.io/client-go/util/retry"
 | 
						"k8s.io/client-go/util/retry"
 | 
				
			||||||
	"k8s.io/kubernetes/test/e2e/feature"
 | 
					 | 
				
			||||||
	"k8s.io/kubernetes/test/e2e/framework"
 | 
						"k8s.io/kubernetes/test/e2e/framework"
 | 
				
			||||||
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
 | 
						e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
 | 
				
			||||||
	e2epv "k8s.io/kubernetes/test/e2e/framework/pv"
 | 
						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.
 | 
								// 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)
 | 
									pvObj := e2epv.MakePersistentVolume(pvConfig)
 | 
				
			||||||
				pv, err = e2epv.CreatePV(ctx, c, f.Timeouts, pvObj)
 | 
									pv, err = e2epv.CreatePV(ctx, c, f.Timeouts, pvObj)
 | 
				
			||||||
				framework.ExpectNoError(err)
 | 
									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
 | 
								// 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.
 | 
								// 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)
 | 
									pv, pvc, err = e2epv.CreatePVPVC(ctx, c, f.Timeouts, pvConfig, pvcConfig, ns, true)
 | 
				
			||||||
				framework.ExpectNoError(err)
 | 
									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
 | 
								// 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
 | 
								// 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.
 | 
								// 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)
 | 
									pv, pvc, err = e2epv.CreatePVPVC(ctx, c, f.Timeouts, pvConfig, pvcConfig, ns, true)
 | 
				
			||||||
				framework.ExpectNoError(err)
 | 
									framework.ExpectNoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user