Skip to content

Commit

Permalink
Merge pull request kubernetes#116469 from RomanBednar/pv-phase-transi…
Browse files Browse the repository at this point in the history
…tion-time

PersistentVolume last phase transition time
  • Loading branch information
k8s-ci-robot committed Jul 21, 2023
2 parents 5f380d5 + 294f5c9 commit f3a070f
Show file tree
Hide file tree
Showing 22 changed files with 1,499 additions and 887 deletions.
4 changes: 4 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.

8 changes: 8 additions & 0 deletions api/openapi-spec/v3/api__v1_openapi.json
Expand Up @@ -4621,6 +4621,14 @@
"io.k8s.api.core.v1.PersistentVolumeStatus": {
"description": "PersistentVolumeStatus is the current status of a persistent volume.",
"properties": {
"lastPhaseTransitionTime": {
"allOf": [
{
"$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 an alpha field and requires enabling PersistentVolumeLastPhaseTransitionTime feature."
},
"message": {
"description": "message is a human-readable message indicating details about why the volume is in this state.",
"type": "string"
Expand Down
12 changes: 10 additions & 2 deletions pkg/api/persistentvolume/util.go
Expand Up @@ -31,16 +31,24 @@ const (
deprecatedStorageClassAnnotationsMsg = `deprecated since v1.8; use "storageClassName" attribute instead`
)

// DropDisabledFields removes disabled fields from the pv spec.
// DropDisabledSpecFields removes disabled fields from the pv spec.
// This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a pv spec.
func DropDisabledFields(pvSpec *api.PersistentVolumeSpec, oldPVSpec *api.PersistentVolumeSpec) {
func DropDisabledSpecFields(pvSpec *api.PersistentVolumeSpec, oldPVSpec *api.PersistentVolumeSpec) {
if !utilfeature.DefaultFeatureGate.Enabled(features.CSINodeExpandSecret) && !hasNodeExpansionSecrets(oldPVSpec) {
if pvSpec.CSI != nil {
pvSpec.CSI.NodeExpandSecretRef = nil
}
}
}

// 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 hasNodeExpansionSecrets(oldPVSpec *api.PersistentVolumeSpec) bool {
if oldPVSpec == nil || oldPVSpec.CSI == nil {
return false
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/persistentvolume/util_test.go
Expand Up @@ -91,7 +91,7 @@ func TestDropDisabledFields(t *testing.T) {
t.Run(name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSINodeExpandSecret, tc.csiExpansionEnabled)()

DropDisabledFields(tc.newSpec, tc.oldSpec)
DropDisabledSpecFields(tc.newSpec, tc.oldSpec)
if !reflect.DeepEqual(tc.newSpec, tc.expectNewSpec) {
t.Error(cmp.Diff(tc.newSpec, tc.expectNewSpec))
}
Expand Down
6 changes: 6 additions & 0 deletions pkg/apis/core/types.go
Expand Up @@ -380,6 +380,12 @@ type PersistentVolumeStatus struct {
// Reason is a brief CamelCase string that describes any failure and is meant for machine parsing and tidy display in the CLI
// +optional
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 an alpha field and requires enabling PersistentVolumeLastPhaseTransitionTime feature.
// +featureGate=PersistentVolumeLastPhaseTransitionTime
// +optional
LastPhaseTransitionTime *metav1.Time
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/core/v1/zz_generated.conversion.go

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

6 changes: 5 additions & 1 deletion pkg/apis/core/zz_generated.deepcopy.go

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

9 changes: 9 additions & 0 deletions pkg/features/kube_features.go
Expand Up @@ -626,6 +626,13 @@ const (
// Enables PDBUnhealthyPodEvictionPolicy for PodDisruptionBudgets
PDBUnhealthyPodEvictionPolicy featuregate.Feature = "PDBUnhealthyPodEvictionPolicy"

// owner: @RomanBednar
// kep: https://kep.k8s.io/3762
// alpha: v1.28
//
// Adds a new field to persistent volumes which holds a timestamp of when the volume last transitioned its phase.
PersistentVolumeLastPhaseTransitionTime featuregate.Feature = "PersistentVolumeLastPhaseTransitionTime"

// owner: @haircommander
// kep: https://kep.k8s.io/2364
// alpha: v1.23
Expand Down Expand Up @@ -1104,6 +1111,8 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS

PDBUnhealthyPodEvictionPolicy: {Default: true, PreRelease: featuregate.Beta},

PersistentVolumeLastPhaseTransitionTime: {Default: false, PreRelease: featuregate.Alpha},

PodAndContainerStatsFromCRI: {Default: false, PreRelease: featuregate.Alpha},

PodDeletionCost: {Default: true, PreRelease: featuregate.Beta},
Expand Down
8 changes: 8 additions & 0 deletions pkg/generated/openapi/zz_generated.openapi.go

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

30 changes: 28 additions & 2 deletions pkg/registry/core/persistentvolume/strategy.go
Expand Up @@ -19,6 +19,9 @@ package persistentvolume
import (
"context"
"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"
Expand Down Expand Up @@ -66,7 +69,13 @@ func (persistentvolumeStrategy) PrepareForCreate(ctx context.Context, obj runtim
pv := obj.(*api.PersistentVolume)
pv.Status = api.PersistentVolumeStatus{}

pvutil.DropDisabledFields(&pv.Spec, nil)
if utilfeature.DefaultFeatureGate.Enabled(features.PersistentVolumeLastPhaseTransitionTime) {
pv.Status.Phase = api.VolumePending
now := nowFunc()
pv.Status.LastPhaseTransitionTime = &now
}

pvutil.DropDisabledSpecFields(&pv.Spec, nil)
}

func (persistentvolumeStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
Expand Down Expand Up @@ -95,7 +104,7 @@ func (persistentvolumeStrategy) PrepareForUpdate(ctx context.Context, obj, old r
oldPv := old.(*api.PersistentVolume)
newPv.Status = oldPv.Status

pvutil.DropDisabledFields(&newPv.Spec, &oldPv.Spec)
pvutil.DropDisabledSpecFields(&newPv.Spec, &oldPv.Spec)
}

func (persistentvolumeStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
Expand Down Expand Up @@ -134,11 +143,28 @@ func (persistentvolumeStatusStrategy) GetResetFields() map[fieldpath.APIVersion]
return fields
}

var nowFunc = metav1.Now

// PrepareForUpdate sets the Spec field which is not allowed to be changed when updating a PV's Status
func (persistentvolumeStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newPv := obj.(*api.PersistentVolume)
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

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 {
Expand Down

0 comments on commit f3a070f

Please sign in to comment.