use pointer types for metav1.Time fields (#1951)

* use pointer types for metav1.Time fields

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* simpler metav1.Time ptrs

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* remove test debug println

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
This commit is contained in:
Adnan Abdulhussein
2019-10-14 09:20:28 -07:00
committed by Steve Kriss
parent 3fc4097231
commit e3d64d9dd9
29 changed files with 158 additions and 88 deletions

View File

@@ -219,7 +219,7 @@ type BackupStatus struct {
// Expiration is when this Backup is eligible for garbage-collection. // Expiration is when this Backup is eligible for garbage-collection.
// +optional // +optional
// +nullable // +nullable
Expiration metav1.Time `json:"expiration,omitempty"` Expiration *metav1.Time `json:"expiration,omitempty"`
// Phase is the current state of the Backup. // Phase is the current state of the Backup.
// +optional // +optional
@@ -237,7 +237,7 @@ type BackupStatus struct {
// The server's time is used for StartTimestamps // The server's time is used for StartTimestamps
// +optional // +optional
// +nullable // +nullable
StartTimestamp metav1.Time `json:"startTimestamp,omitempty"` StartTimestamp *metav1.Time `json:"startTimestamp,omitempty"`
// CompletionTimestamp records the time a backup was completed. // CompletionTimestamp records the time a backup was completed.
// Completion time is recorded even on failed backups. // Completion time is recorded even on failed backups.
@@ -245,7 +245,7 @@ type BackupStatus struct {
// The server's time is used for CompletionTimestamps // The server's time is used for CompletionTimestamps
// +optional // +optional
// +nullable // +nullable
CompletionTimestamp metav1.Time `json:"completionTimestamp,omitempty"` CompletionTimestamp *metav1.Time `json:"completionTimestamp,omitempty"`
// VolumeSnapshotsAttempted is the total number of attempted // VolumeSnapshotsAttempted is the total number of attempted
// volume snapshots for this backup. // volume snapshots for this backup.

View File

@@ -119,7 +119,7 @@ type BackupStorageLocationStatus struct {
// the cluster. // the cluster.
// +optional // +optional
// +nullable // +nullable
LastSyncedTime metav1.Time `json:"lastSyncedTime,omitempty"` LastSyncedTime *metav1.Time `json:"lastSyncedTime,omitempty"`
// LastSyncedRevision is the value of the `metadata/revision` file in the backup // LastSyncedRevision is the value of the `metadata/revision` file in the backup
// storage location the last time the BSL's contents were synced into the cluster. // storage location the last time the BSL's contents were synced into the cluster.

View File

@@ -74,7 +74,7 @@ type DownloadRequestStatus struct {
// Expiration is when this DownloadRequest expires and can be deleted by the system. // Expiration is when this DownloadRequest expires and can be deleted by the system.
// +optional // +optional
// +nullable // +nullable
Expiration metav1.Time `json:"expiration,omitempty"` Expiration *metav1.Time `json:"expiration,omitempty"`
} }
// +genclient // +genclient

View File

@@ -81,7 +81,7 @@ type PodVolumeBackupStatus struct {
// The server's time is used for StartTimestamps // The server's time is used for StartTimestamps
// +optional // +optional
// +nullable // +nullable
StartTimestamp metav1.Time `json:"startTimestamp,omitempty"` StartTimestamp *metav1.Time `json:"startTimestamp,omitempty"`
// CompletionTimestamp records the time a backup was completed. // CompletionTimestamp records the time a backup was completed.
// Completion time is recorded even on failed backups. // Completion time is recorded even on failed backups.
@@ -89,7 +89,7 @@ type PodVolumeBackupStatus struct {
// The server's time is used for CompletionTimestamps // The server's time is used for CompletionTimestamps
// +optional // +optional
// +nullable // +nullable
CompletionTimestamp metav1.Time `json:"completionTimestamp,omitempty"` CompletionTimestamp *metav1.Time `json:"completionTimestamp,omitempty"`
// Progress holds the total number of bytes of the volume and the current // Progress holds the total number of bytes of the volume and the current
// number of backed up bytes. This can be used to display progress information // number of backed up bytes. This can be used to display progress information

View File

@@ -65,14 +65,14 @@ type PodVolumeRestoreStatus struct {
// The server's time is used for StartTimestamps // The server's time is used for StartTimestamps
// +optional // +optional
// +nullable // +nullable
StartTimestamp metav1.Time `json:"startTimestamp,omitempty"` StartTimestamp *metav1.Time `json:"startTimestamp,omitempty"`
// CompletionTimestamp records the time a restore was completed. // CompletionTimestamp records the time a restore was completed.
// Completion time is recorded even on failed restores. // Completion time is recorded even on failed restores.
// The server's time is used for CompletionTimestamps // The server's time is used for CompletionTimestamps
// +optional // +optional
// +nullable // +nullable
CompletionTimestamp metav1.Time `json:"completionTimestamp,omitempty"` CompletionTimestamp *metav1.Time `json:"completionTimestamp,omitempty"`
// Progress holds the total number of bytes of the snapshot and the current // Progress holds the total number of bytes of the snapshot and the current
// number of restored bytes. This can be used to display progress information // number of restored bytes. This can be used to display progress information

View File

@@ -61,7 +61,7 @@ type ResticRepositoryStatus struct {
// LastMaintenanceTime is the last time maintenance was run. // LastMaintenanceTime is the last time maintenance was run.
// +optional // +optional
// +nullable // +nullable
LastMaintenanceTime metav1.Time `json:"lastMaintenanceTime,omitempty"` LastMaintenanceTime *metav1.Time `json:"lastMaintenanceTime,omitempty"`
} }
// +genclient // +genclient

View File

@@ -58,7 +58,7 @@ type ScheduleStatus struct {
// Schedule schedule // Schedule schedule
// +optional // +optional
// +nullable // +nullable
LastBackup metav1.Time `json:"lastBackup,omitempty"` LastBackup *metav1.Time `json:"lastBackup,omitempty"`
// ValidationErrors is a slice of all validation errors (if // ValidationErrors is a slice of all validation errors (if
// applicable) // applicable)

View File

@@ -63,22 +63,22 @@ type PluginInfo struct {
type ServerStatusRequestStatus struct { type ServerStatusRequestStatus struct {
// Phase is the current lifecycle phase of the ServerStatusRequest. // Phase is the current lifecycle phase of the ServerStatusRequest.
// +optional // +optional
Phase ServerStatusRequestPhase `json:"phase"` Phase ServerStatusRequestPhase `json:"phase,omitempty"`
// ProcessedTimestamp is when the ServerStatusRequest was processed // ProcessedTimestamp is when the ServerStatusRequest was processed
// by the ServerStatusRequestController. // by the ServerStatusRequestController.
// +optional // +optional
// +nullable // +nullable
ProcessedTimestamp metav1.Time `json:"processedTimestamp"` ProcessedTimestamp *metav1.Time `json:"processedTimestamp,omitempty"`
// ServerVersion is the Velero server version. // ServerVersion is the Velero server version.
// +optional // +optional
ServerVersion string `json:"serverVersion"` ServerVersion string `json:"serverVersion,omitempty"`
// Plugins list information about the plugins running on the Velero server // Plugins list information about the plugins running on the Velero server
// +optional // +optional
// +nullable // +nullable
Plugins []PluginInfo `json:"plugins"` Plugins []PluginInfo `json:"plugins,omitempty"`
} }
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

View File

@@ -246,14 +246,23 @@ func (in *BackupSpec) DeepCopy() *BackupSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BackupStatus) DeepCopyInto(out *BackupStatus) { func (in *BackupStatus) DeepCopyInto(out *BackupStatus) {
*out = *in *out = *in
in.Expiration.DeepCopyInto(&out.Expiration) if in.Expiration != nil {
in, out := &in.Expiration, &out.Expiration
*out = (*in).DeepCopy()
}
if in.ValidationErrors != nil { if in.ValidationErrors != nil {
in, out := &in.ValidationErrors, &out.ValidationErrors in, out := &in.ValidationErrors, &out.ValidationErrors
*out = make([]string, len(*in)) *out = make([]string, len(*in))
copy(*out, *in) copy(*out, *in)
} }
in.StartTimestamp.DeepCopyInto(&out.StartTimestamp) if in.StartTimestamp != nil {
in.CompletionTimestamp.DeepCopyInto(&out.CompletionTimestamp) in, out := &in.StartTimestamp, &out.StartTimestamp
*out = (*in).DeepCopy()
}
if in.CompletionTimestamp != nil {
in, out := &in.CompletionTimestamp, &out.CompletionTimestamp
*out = (*in).DeepCopy()
}
return return
} }
@@ -355,7 +364,10 @@ func (in *BackupStorageLocationSpec) DeepCopy() *BackupStorageLocationSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BackupStorageLocationStatus) DeepCopyInto(out *BackupStorageLocationStatus) { func (in *BackupStorageLocationStatus) DeepCopyInto(out *BackupStorageLocationStatus) {
*out = *in *out = *in
in.LastSyncedTime.DeepCopyInto(&out.LastSyncedTime) if in.LastSyncedTime != nil {
in, out := &in.LastSyncedTime, &out.LastSyncedTime
*out = (*in).DeepCopy()
}
return return
} }
@@ -548,7 +560,10 @@ func (in *DownloadRequestSpec) DeepCopy() *DownloadRequestSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DownloadRequestStatus) DeepCopyInto(out *DownloadRequestStatus) { func (in *DownloadRequestStatus) DeepCopyInto(out *DownloadRequestStatus) {
*out = *in *out = *in
in.Expiration.DeepCopyInto(&out.Expiration) if in.Expiration != nil {
in, out := &in.Expiration, &out.Expiration
*out = (*in).DeepCopy()
}
return return
} }
@@ -720,8 +735,14 @@ func (in *PodVolumeBackupSpec) DeepCopy() *PodVolumeBackupSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PodVolumeBackupStatus) DeepCopyInto(out *PodVolumeBackupStatus) { func (in *PodVolumeBackupStatus) DeepCopyInto(out *PodVolumeBackupStatus) {
*out = *in *out = *in
in.StartTimestamp.DeepCopyInto(&out.StartTimestamp) if in.StartTimestamp != nil {
in.CompletionTimestamp.DeepCopyInto(&out.CompletionTimestamp) in, out := &in.StartTimestamp, &out.StartTimestamp
*out = (*in).DeepCopy()
}
if in.CompletionTimestamp != nil {
in, out := &in.CompletionTimestamp, &out.CompletionTimestamp
*out = (*in).DeepCopy()
}
out.Progress = in.Progress out.Progress = in.Progress
return return
} }
@@ -833,8 +854,14 @@ func (in *PodVolumeRestoreSpec) DeepCopy() *PodVolumeRestoreSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PodVolumeRestoreStatus) DeepCopyInto(out *PodVolumeRestoreStatus) { func (in *PodVolumeRestoreStatus) DeepCopyInto(out *PodVolumeRestoreStatus) {
*out = *in *out = *in
in.StartTimestamp.DeepCopyInto(&out.StartTimestamp) if in.StartTimestamp != nil {
in.CompletionTimestamp.DeepCopyInto(&out.CompletionTimestamp) in, out := &in.StartTimestamp, &out.StartTimestamp
*out = (*in).DeepCopy()
}
if in.CompletionTimestamp != nil {
in, out := &in.CompletionTimestamp, &out.CompletionTimestamp
*out = (*in).DeepCopy()
}
out.Progress = in.Progress out.Progress = in.Progress
return return
} }
@@ -930,7 +957,10 @@ func (in *ResticRepositorySpec) DeepCopy() *ResticRepositorySpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResticRepositoryStatus) DeepCopyInto(out *ResticRepositoryStatus) { func (in *ResticRepositoryStatus) DeepCopyInto(out *ResticRepositoryStatus) {
*out = *in *out = *in
in.LastMaintenanceTime.DeepCopyInto(&out.LastMaintenanceTime) if in.LastMaintenanceTime != nil {
in, out := &in.LastMaintenanceTime, &out.LastMaintenanceTime
*out = (*in).DeepCopy()
}
return return
} }
@@ -1165,7 +1195,10 @@ func (in *ScheduleSpec) DeepCopy() *ScheduleSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ScheduleStatus) DeepCopyInto(out *ScheduleStatus) { func (in *ScheduleStatus) DeepCopyInto(out *ScheduleStatus) {
*out = *in *out = *in
in.LastBackup.DeepCopyInto(&out.LastBackup) if in.LastBackup != nil {
in, out := &in.LastBackup, &out.LastBackup
*out = (*in).DeepCopy()
}
if in.ValidationErrors != nil { if in.ValidationErrors != nil {
in, out := &in.ValidationErrors, &out.ValidationErrors in, out := &in.ValidationErrors, &out.ValidationErrors
*out = make([]string, len(*in)) *out = make([]string, len(*in))
@@ -1264,7 +1297,10 @@ func (in *ServerStatusRequestSpec) DeepCopy() *ServerStatusRequestSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ServerStatusRequestStatus) DeepCopyInto(out *ServerStatusRequestStatus) { func (in *ServerStatusRequestStatus) DeepCopyInto(out *ServerStatusRequestStatus) {
*out = *in *out = *in
in.ProcessedTimestamp.DeepCopyInto(&out.ProcessedTimestamp) if in.ProcessedTimestamp != nil {
in, out := &in.ProcessedTimestamp, &out.ProcessedTimestamp
*out = (*in).DeepCopy()
}
if in.Plugins != nil { if in.Plugins != nil {
in, out := &in.Plugins, &out.Plugins in, out := &in.Plugins, &out.Plugins
*out = make([]PluginInfo, len(*in)) *out = make([]PluginInfo, len(*in))

View File

@@ -154,13 +154,13 @@ func (b *BackupBuilder) TTL(ttl time.Duration) *BackupBuilder {
// Expiration sets the Backup's expiration. // Expiration sets the Backup's expiration.
func (b *BackupBuilder) Expiration(val time.Time) *BackupBuilder { func (b *BackupBuilder) Expiration(val time.Time) *BackupBuilder {
b.object.Status.Expiration.Time = val b.object.Status.Expiration = &metav1.Time{Time: val}
return b return b
} }
// StartTimestamp sets the Backup's start timestamp. // StartTimestamp sets the Backup's start timestamp.
func (b *BackupBuilder) StartTimestamp(val time.Time) *BackupBuilder { func (b *BackupBuilder) StartTimestamp(val time.Time) *BackupBuilder {
b.object.Status.StartTimestamp.Time = val b.object.Status.StartTimestamp = &metav1.Time{Time: val}
return b return b
} }

View File

@@ -80,7 +80,7 @@ func (b *ScheduleBuilder) CronSchedule(expression string) *ScheduleBuilder {
// LastBackupTime sets the Schedule's last backup time. // LastBackupTime sets the Schedule's last backup time.
func (b *ScheduleBuilder) LastBackupTime(val string) *ScheduleBuilder { func (b *ScheduleBuilder) LastBackupTime(val string) *ScheduleBuilder {
t, _ := time.Parse("2006-01-02 15:04:05", val) t, _ := time.Parse("2006-01-02 15:04:05", val)
b.object.Status.LastBackup.Time = t b.object.Status.LastBackup = &metav1.Time{Time: t}
return b return b
} }

View File

@@ -67,7 +67,7 @@ func (b *ServerStatusRequestBuilder) Phase(phase velerov1api.ServerStatusRequest
// ProcessedTimestamp sets the ServerStatusRequest's processed timestamp. // ProcessedTimestamp sets the ServerStatusRequest's processed timestamp.
func (b *ServerStatusRequestBuilder) ProcessedTimestamp(time time.Time) *ServerStatusRequestBuilder { func (b *ServerStatusRequestBuilder) ProcessedTimestamp(time time.Time) *ServerStatusRequestBuilder {
b.object.Status.ProcessedTimestamp.Time = time b.object.Status.ProcessedTimestamp = &metav1.Time{Time: time}
return b return b
} }

View File

@@ -219,19 +219,22 @@ func DescribeBackupStatus(d *Describer, backup *velerov1api.Backup, details bool
d.Println() d.Println()
// "<n/a>" output should only be applicable for backups that failed validation // "<n/a>" output should only be applicable for backups that failed validation
if status.StartTimestamp.Time.IsZero() { if status.StartTimestamp == nil || status.StartTimestamp.Time.IsZero() {
d.Printf("Started:\t%s\n", "<n/a>") d.Printf("Started:\t%s\n", "<n/a>")
} else { } else {
d.Printf("Started:\t%s\n", status.StartTimestamp.Time) d.Printf("Started:\t%s\n", status.StartTimestamp.Time)
} }
if status.CompletionTimestamp.Time.IsZero() { if status.CompletionTimestamp == nil || status.CompletionTimestamp.Time.IsZero() {
d.Printf("Completed:\t%s\n", "<n/a>") d.Printf("Completed:\t%s\n", "<n/a>")
} else { } else {
d.Printf("Completed:\t%s\n", status.CompletionTimestamp.Time) d.Printf("Completed:\t%s\n", status.CompletionTimestamp.Time)
} }
d.Println() d.Println()
d.Printf("Expiration:\t%s\n", status.Expiration.Time) // Expiration can't be 0, it is always set to a 30-day default. It can be nil
// if the controller hasn't processed this Backup yet, in which case this will
// just display `<nil>`, though this should be temporary.
d.Printf("Expiration:\t%s\n", status.Expiration)
d.Println() d.Println()
if details { if details {

View File

@@ -88,7 +88,10 @@ func printBackup(backup *velerov1api.Backup, options printers.PrintOptions) ([]m
Object: runtime.RawExtension{Object: backup}, Object: runtime.RawExtension{Object: backup},
} }
expiration := backup.Status.Expiration.Time var expiration time.Time
if backup.Status.Expiration != nil {
expiration = backup.Status.Expiration.Time
}
if expiration.IsZero() && backup.Spec.TTL.Duration > 0 { if expiration.IsZero() && backup.Spec.TTL.Duration > 0 {
expiration = backup.CreationTimestamp.Add(backup.Spec.TTL.Duration) expiration = backup.CreationTimestamp.Add(backup.Spec.TTL.Duration)
} }
@@ -111,7 +114,7 @@ func printBackup(backup *velerov1api.Backup, options printers.PrintOptions) ([]m
location := backup.Spec.StorageLocation location := backup.Spec.StorageLocation
row.Cells = append(row.Cells, backup.Name, status, backup.Status.StartTimestamp.Time, humanReadableTimeFromNow(expiration), location, metav1.FormatLabelSelector(backup.Spec.LabelSelector)) row.Cells = append(row.Cells, backup.Name, status, backup.Status.StartTimestamp, humanReadableTimeFromNow(expiration), location, metav1.FormatLabelSelector(backup.Spec.LabelSelector))
return []metav1.TableRow{row}, nil return []metav1.TableRow{row}, nil
} }

View File

@@ -62,7 +62,7 @@ func DescribeScheduleSpec(d *Describer, spec v1.ScheduleSpec) {
func DescribeScheduleStatus(d *Describer, status v1.ScheduleStatus) { func DescribeScheduleStatus(d *Describer, status v1.ScheduleStatus) {
lastBackup := "<never>" lastBackup := "<never>"
if !status.LastBackup.Time.IsZero() { if status.LastBackup != nil && !status.LastBackup.Time.IsZero() {
lastBackup = fmt.Sprintf("%v", status.LastBackup.Time) lastBackup = fmt.Sprintf("%v", status.LastBackup.Time)
} }
d.Printf("Last Backup:\t%s\n", lastBackup) d.Printf("Last Backup:\t%s\n", lastBackup)

View File

@@ -17,6 +17,8 @@ limitations under the License.
package output package output
import ( import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/printers"
@@ -61,13 +63,18 @@ func printSchedule(schedule *v1.Schedule, options printers.PrintOptions) ([]meta
status = v1.SchedulePhaseNew status = v1.SchedulePhaseNew
} }
var lastBackupTime time.Time
if schedule.Status.LastBackup != nil {
lastBackupTime = schedule.Status.LastBackup.Time
}
row.Cells = append(row.Cells, row.Cells = append(row.Cells,
schedule.Name, schedule.Name,
status, status,
schedule.CreationTimestamp.Time, schedule.CreationTimestamp.Time,
schedule.Spec.Schedule, schedule.Spec.Schedule,
schedule.Spec.Template.TTL.Duration, schedule.Spec.Template.TTL.Duration,
humanReadableTimeFromNow(schedule.Status.LastBackup.Time), humanReadableTimeFromNow(lastBackupTime),
metav1.FormatLabelSelector(schedule.Spec.Template.LabelSelector), metav1.FormatLabelSelector(schedule.Spec.Template.LabelSelector),
) )

View File

@@ -198,7 +198,7 @@ func (c *backupController) processBackup(key string) error {
request.Status.Phase = velerov1api.BackupPhaseFailedValidation request.Status.Phase = velerov1api.BackupPhaseFailedValidation
} else { } else {
request.Status.Phase = velerov1api.BackupPhaseInProgress request.Status.Phase = velerov1api.BackupPhaseInProgress
request.Status.StartTimestamp.Time = c.clock.Now() request.Status.StartTimestamp = &metav1.Time{Time: c.clock.Now()}
} }
// update status // update status
@@ -289,7 +289,7 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg
} }
// calculate expiration // calculate expiration
request.Status.Expiration = metav1.NewTime(c.clock.Now().Add(request.Spec.TTL.Duration)) request.Status.Expiration = &metav1.Time{Time: c.clock.Now().Add(request.Spec.TTL.Duration)}
// default storage location if not specified // default storage location if not specified
if request.Spec.StorageLocation == "" { if request.Spec.StorageLocation == "" {
@@ -485,7 +485,7 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error {
exists, err := backupStore.BackupExists(backup.StorageLocation.Spec.StorageType.ObjectStorage.Bucket, backup.Name) exists, err := backupStore.BackupExists(backup.StorageLocation.Spec.StorageType.ObjectStorage.Bucket, backup.Name)
if exists || err != nil { if exists || err != nil {
backup.Status.Phase = velerov1api.BackupPhaseFailed backup.Status.Phase = velerov1api.BackupPhaseFailed
backup.Status.CompletionTimestamp.Time = c.clock.Now() backup.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()}
if err != nil { if err != nil {
return errors.Wrapf(err, "error checking if backup already exists in object storage") return errors.Wrapf(err, "error checking if backup already exists in object storage")
} }
@@ -499,7 +499,7 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error {
// Mark completion timestamp before serializing and uploading. // Mark completion timestamp before serializing and uploading.
// Otherwise, the JSON file in object storage has a CompletionTimestamp of 'null'. // Otherwise, the JSON file in object storage has a CompletionTimestamp of 'null'.
backup.Status.CompletionTimestamp.Time = c.clock.Now() backup.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()}
backup.Status.VolumeSnapshotsAttempted = len(backup.VolumeSnapshots) backup.Status.VolumeSnapshotsAttempted = len(backup.VolumeSnapshots)
for _, snap := range backup.VolumeSnapshots { for _, snap := range backup.VolumeSnapshots {

View File

@@ -305,7 +305,7 @@ func TestDefaultBackupTTL(t *testing.T) {
res := c.prepareBackupRequest(test.backup) res := c.prepareBackupRequest(test.backup)
assert.NotNil(t, res) assert.NotNil(t, res)
assert.Equal(t, test.expectedTTL, res.Spec.TTL) assert.Equal(t, test.expectedTTL, res.Spec.TTL)
assert.Equal(t, test.expectedExpiration, res.Status.Expiration) assert.Equal(t, test.expectedExpiration, *res.Status.Expiration)
}) })
} }
} }
@@ -316,6 +316,7 @@ func TestProcessBackupCompletions(t *testing.T) {
now, err := time.Parse(time.RFC1123Z, time.RFC1123Z) now, err := time.Parse(time.RFC1123Z, time.RFC1123Z)
require.NoError(t, err) require.NoError(t, err)
now = now.Local() now = now.Local()
timestamp := metav1.NewTime(now)
tests := []struct { tests := []struct {
name string name string
@@ -348,9 +349,9 @@ func TestProcessBackupCompletions(t *testing.T) {
Status: velerov1api.BackupStatus{ Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseCompleted, Phase: velerov1api.BackupPhaseCompleted,
Version: 1, Version: 1,
StartTimestamp: metav1.NewTime(now), StartTimestamp: &timestamp,
CompletionTimestamp: metav1.NewTime(now), CompletionTimestamp: &timestamp,
Expiration: metav1.NewTime(now), Expiration: &timestamp,
}, },
}, },
}, },
@@ -376,9 +377,9 @@ func TestProcessBackupCompletions(t *testing.T) {
Status: velerov1api.BackupStatus{ Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseCompleted, Phase: velerov1api.BackupPhaseCompleted,
Version: 1, Version: 1,
StartTimestamp: metav1.NewTime(now), StartTimestamp: &timestamp,
CompletionTimestamp: metav1.NewTime(now), CompletionTimestamp: &timestamp,
Expiration: metav1.NewTime(now), Expiration: &timestamp,
}, },
}, },
}, },
@@ -407,9 +408,9 @@ func TestProcessBackupCompletions(t *testing.T) {
Status: velerov1api.BackupStatus{ Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseCompleted, Phase: velerov1api.BackupPhaseCompleted,
Version: 1, Version: 1,
StartTimestamp: metav1.NewTime(now), StartTimestamp: &timestamp,
CompletionTimestamp: metav1.NewTime(now), CompletionTimestamp: &timestamp,
Expiration: metav1.NewTime(now), Expiration: &timestamp,
}, },
}, },
}, },
@@ -436,9 +437,9 @@ func TestProcessBackupCompletions(t *testing.T) {
Status: velerov1api.BackupStatus{ Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseCompleted, Phase: velerov1api.BackupPhaseCompleted,
Version: 1, Version: 1,
Expiration: metav1.NewTime(now.Add(10 * time.Minute)), Expiration: &metav1.Time{now.Add(10 * time.Minute)},
StartTimestamp: metav1.NewTime(now), StartTimestamp: &timestamp,
CompletionTimestamp: metav1.NewTime(now), CompletionTimestamp: &timestamp,
}, },
}, },
}, },
@@ -465,9 +466,9 @@ func TestProcessBackupCompletions(t *testing.T) {
Status: velerov1api.BackupStatus{ Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseCompleted, Phase: velerov1api.BackupPhaseCompleted,
Version: 1, Version: 1,
StartTimestamp: metav1.NewTime(now), StartTimestamp: &timestamp,
CompletionTimestamp: metav1.NewTime(now), CompletionTimestamp: &timestamp,
Expiration: metav1.NewTime(now), Expiration: &timestamp,
}, },
}, },
}, },
@@ -496,9 +497,9 @@ func TestProcessBackupCompletions(t *testing.T) {
Status: velerov1api.BackupStatus{ Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFailed, Phase: velerov1api.BackupPhaseFailed,
Version: 1, Version: 1,
StartTimestamp: metav1.NewTime(now), StartTimestamp: &timestamp,
CompletionTimestamp: metav1.NewTime(now), CompletionTimestamp: &timestamp,
Expiration: metav1.NewTime(now), Expiration: &timestamp,
}, },
}, },
}, },
@@ -525,9 +526,9 @@ func TestProcessBackupCompletions(t *testing.T) {
Status: velerov1api.BackupStatus{ Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFailed, Phase: velerov1api.BackupPhaseFailed,
Version: 1, Version: 1,
StartTimestamp: metav1.NewTime(now), StartTimestamp: &timestamp,
CompletionTimestamp: metav1.NewTime(now), CompletionTimestamp: &timestamp,
Expiration: metav1.NewTime(now), Expiration: &timestamp,
}, },
}, },
}, },

View File

@@ -184,7 +184,7 @@ func (c *downloadRequestController) generatePreSignedURL(downloadRequest *v1.Dow
} }
update.Status.Phase = v1.DownloadRequestPhaseProcessed update.Status.Phase = v1.DownloadRequestPhaseProcessed
update.Status.Expiration = metav1.NewTime(c.clock.Now().Add(persistence.DownloadURLTTL)) update.Status.Expiration = &metav1.Time{Time: c.clock.Now().Add(persistence.DownloadURLTTL)}
_, err = patchDownloadRequest(downloadRequest, update, c.downloadRequestClient) _, err = patchDownloadRequest(downloadRequest, update, c.downloadRequestClient)
return errors.WithStack(err) return errors.WithStack(err)

View File

@@ -252,9 +252,9 @@ func TestProcessDownloadRequest(t *testing.T) {
// clock time, it's easier to do this here than as part of the test case definitions. // clock time, it's easier to do this here than as part of the test case definitions.
if tc.downloadRequest != nil && tc.downloadRequest.Status.Phase == v1.DownloadRequestPhaseProcessed { if tc.downloadRequest != nil && tc.downloadRequest.Status.Phase == v1.DownloadRequestPhaseProcessed {
if tc.expired { if tc.expired {
tc.downloadRequest.Status.Expiration.Time = harness.controller.clock.Now().Add(-1 * time.Minute) tc.downloadRequest.Status.Expiration = &metav1.Time{Time: harness.controller.clock.Now().Add(-1 * time.Minute)}
} else { } else {
tc.downloadRequest.Status.Expiration.Time = harness.controller.clock.Now().Add(time.Minute) tc.downloadRequest.Status.Expiration = &metav1.Time{Time: harness.controller.clock.Now().Add(time.Minute)}
} }
} }

View File

@@ -123,14 +123,13 @@ func (c *gcController) processQueueItem(key string) error {
log = c.logger.WithFields( log = c.logger.WithFields(
logrus.Fields{ logrus.Fields{
"backup": key, "backup": key,
"expiration": backup.Status.Expiration.Time, "expiration": backup.Status.Expiration,
}, },
) )
now := c.clock.Now() now := c.clock.Now()
expiration := backup.Status.Expiration.Time if backup.Status.Expiration == nil || backup.Status.Expiration.After(now) {
if expiration.IsZero() || expiration.After(now) {
log.Debug("Backup has not expired yet, skipping") log.Debug("Backup has not expired yet, skipping")
return nil return nil
} }

View File

@@ -27,6 +27,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/clock" "k8s.io/apimachinery/pkg/util/clock"
@@ -181,7 +182,7 @@ func (c *podVolumeBackupController) processBackup(req *velerov1api.PodVolumeBack
// update status to InProgress // update status to InProgress
req, err = c.patchPodVolumeBackup(req, func(r *velerov1api.PodVolumeBackup) { req, err = c.patchPodVolumeBackup(req, func(r *velerov1api.PodVolumeBackup) {
r.Status.Phase = velerov1api.PodVolumeBackupPhaseInProgress r.Status.Phase = velerov1api.PodVolumeBackupPhaseInProgress
r.Status.StartTimestamp.Time = c.clock.Now() r.Status.StartTimestamp = &metav1.Time{Time: c.clock.Now()}
}) })
if err != nil { if err != nil {
log.WithError(err).Error("Error setting PodVolumeBackup StartTimestamp and phase to InProgress") log.WithError(err).Error("Error setting PodVolumeBackup StartTimestamp and phase to InProgress")
@@ -277,7 +278,7 @@ func (c *podVolumeBackupController) processBackup(req *velerov1api.PodVolumeBack
r.Status.Path = path r.Status.Path = path
r.Status.Phase = velerov1api.PodVolumeBackupPhaseCompleted r.Status.Phase = velerov1api.PodVolumeBackupPhaseCompleted
r.Status.SnapshotID = snapshotID r.Status.SnapshotID = snapshotID
r.Status.CompletionTimestamp.Time = c.clock.Now() r.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()}
if emptySnapshot { if emptySnapshot {
r.Status.Message = "volume was empty so no snapshot was taken" r.Status.Message = "volume was empty so no snapshot was taken"
} }
@@ -376,7 +377,7 @@ func (c *podVolumeBackupController) fail(req *velerov1api.PodVolumeBackup, msg s
if _, err := c.patchPodVolumeBackup(req, func(r *velerov1api.PodVolumeBackup) { if _, err := c.patchPodVolumeBackup(req, func(r *velerov1api.PodVolumeBackup) {
r.Status.Phase = velerov1api.PodVolumeBackupPhaseFailed r.Status.Phase = velerov1api.PodVolumeBackupPhaseFailed
r.Status.Message = msg r.Status.Message = msg
r.Status.CompletionTimestamp.Time = c.clock.Now() r.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()}
}); err != nil { }); err != nil {
log.WithError(err).Error("Error setting PodVolumeBackup phase to Failed") log.WithError(err).Error("Error setting PodVolumeBackup phase to Failed")
return err return err

View File

@@ -29,6 +29,7 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
corev1api "k8s.io/api/core/v1" corev1api "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/clock" "k8s.io/apimachinery/pkg/util/clock"
@@ -265,7 +266,7 @@ func (c *podVolumeRestoreController) processRestore(req *velerov1api.PodVolumeRe
// update status to InProgress // update status to InProgress
req, err = c.patchPodVolumeRestore(req, func(r *velerov1api.PodVolumeRestore) { req, err = c.patchPodVolumeRestore(req, func(r *velerov1api.PodVolumeRestore) {
r.Status.Phase = velerov1api.PodVolumeRestorePhaseInProgress r.Status.Phase = velerov1api.PodVolumeRestorePhaseInProgress
r.Status.StartTimestamp.Time = c.clock.Now() r.Status.StartTimestamp = &metav1.Time{Time: c.clock.Now()}
}) })
if err != nil { if err != nil {
log.WithError(err).Error("Error setting PodVolumeRestore startTimestamp and phase to InProgress") log.WithError(err).Error("Error setting PodVolumeRestore startTimestamp and phase to InProgress")
@@ -301,7 +302,7 @@ func (c *podVolumeRestoreController) processRestore(req *velerov1api.PodVolumeRe
// update status to Completed // update status to Completed
if _, err = c.patchPodVolumeRestore(req, func(r *velerov1api.PodVolumeRestore) { if _, err = c.patchPodVolumeRestore(req, func(r *velerov1api.PodVolumeRestore) {
r.Status.Phase = velerov1api.PodVolumeRestorePhaseCompleted r.Status.Phase = velerov1api.PodVolumeRestorePhaseCompleted
r.Status.CompletionTimestamp.Time = c.clock.Now() r.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()}
}); err != nil { }); err != nil {
log.WithError(err).Error("Error setting PodVolumeRestore completionTimestamp and phase to Completed") log.WithError(err).Error("Error setting PodVolumeRestore completionTimestamp and phase to Completed")
return err return err
@@ -408,7 +409,7 @@ func (c *podVolumeRestoreController) failRestore(req *velerov1api.PodVolumeResto
if _, err := c.patchPodVolumeRestore(req, func(pvr *velerov1api.PodVolumeRestore) { if _, err := c.patchPodVolumeRestore(req, func(pvr *velerov1api.PodVolumeRestore) {
pvr.Status.Phase = velerov1api.PodVolumeRestorePhaseFailed pvr.Status.Phase = velerov1api.PodVolumeRestorePhaseFailed
pvr.Status.Message = msg pvr.Status.Message = msg
pvr.Status.CompletionTimestamp.Time = c.clock.Now() pvr.Status.CompletionTimestamp = &metav1.Time{Time: c.clock.Now()}
}); err != nil { }); err != nil {
log.WithError(err).Error("Error setting PodVolumeRestore phase to Failed") log.WithError(err).Error("Error setting PodVolumeRestore phase to Failed")
return err return err

View File

@@ -190,7 +190,7 @@ func (c *resticRepositoryController) initializeRepo(req *v1.ResticRepository, lo
return c.patchResticRepository(req, func(req *v1.ResticRepository) { return c.patchResticRepository(req, func(req *v1.ResticRepository) {
req.Status.Phase = v1.ResticRepositoryPhaseReady req.Status.Phase = v1.ResticRepositoryPhaseReady
req.Status.LastMaintenanceTime = metav1.Time{Time: time.Now()} req.Status.LastMaintenanceTime = &metav1.Time{Time: time.Now()}
}) })
} }
@@ -238,7 +238,7 @@ func (c *resticRepositoryController) runMaintenanceIfDue(req *v1.ResticRepositor
} }
return c.patchResticRepository(req, func(req *v1.ResticRepository) { return c.patchResticRepository(req, func(req *v1.ResticRepository) {
req.Status.LastMaintenanceTime = metav1.Time{Time: now} req.Status.LastMaintenanceTime = &metav1.Time{Time: now}
}) })
} }

View File

@@ -373,7 +373,15 @@ func backupXorScheduleProvided(restore *api.Restore) bool {
func mostRecentCompletedBackup(backups []*api.Backup) *api.Backup { func mostRecentCompletedBackup(backups []*api.Backup) *api.Backup {
sort.Slice(backups, func(i, j int) bool { sort.Slice(backups, func(i, j int) bool {
// Use .After() because we want descending sort. // Use .After() because we want descending sort.
return backups[i].Status.StartTimestamp.After(backups[j].Status.StartTimestamp.Time)
var iStartTime, jStartTime time.Time
if backups[i].Status.StartTimestamp != nil {
iStartTime = backups[i].Status.StartTimestamp.Time
}
if backups[j].Status.StartTimestamp != nil {
jStartTime = backups[j].Status.StartTimestamp.Time
}
return iStartTime.After(jStartTime)
}) })
for _, backup := range backups { for _, backup := range backups {

View File

@@ -781,7 +781,7 @@ func TestMostRecentCompletedBackup(t *testing.T) {
}, },
Status: api.BackupStatus{ Status: api.BackupStatus{
Phase: api.BackupPhaseCompleted, Phase: api.BackupPhaseCompleted,
StartTimestamp: metav1.Time{Time: now}, StartTimestamp: &metav1.Time{Time: now},
}, },
}) })
@@ -791,7 +791,7 @@ func TestMostRecentCompletedBackup(t *testing.T) {
}, },
Status: api.BackupStatus{ Status: api.BackupStatus{
Phase: api.BackupPhaseCompleted, Phase: api.BackupPhaseCompleted,
StartTimestamp: metav1.Time{Time: now.Add(time.Second)}, StartTimestamp: &metav1.Time{Time: now.Add(time.Second)},
}, },
} }
backups = append(backups, expected) backups = append(backups, expected)

View File

@@ -266,7 +266,7 @@ func (c *scheduleController) submitBackupIfDue(item *api.Schedule, cronSchedule
original := item original := item
schedule := item.DeepCopy() schedule := item.DeepCopy()
schedule.Status.LastBackup = metav1.NewTime(now) schedule.Status.LastBackup = &metav1.Time{Time: now}
if _, err := patchSchedule(original, schedule, c.schedulesClient); err != nil { if _, err := patchSchedule(original, schedule, c.schedulesClient); err != nil {
return errors.Wrapf(err, "error updating Schedule's LastBackup time to %v", schedule.Status.LastBackup) return errors.Wrapf(err, "error updating Schedule's LastBackup time to %v", schedule.Status.LastBackup)
@@ -278,7 +278,10 @@ func (c *scheduleController) submitBackupIfDue(item *api.Schedule, cronSchedule
func getNextRunTime(schedule *api.Schedule, cronSchedule cron.Schedule, asOf time.Time) (bool, time.Time) { func getNextRunTime(schedule *api.Schedule, cronSchedule cron.Schedule, asOf time.Time) (bool, time.Time) {
// get the latest run time (if the schedule hasn't run yet, this will be the zero value which will trigger // get the latest run time (if the schedule hasn't run yet, this will be the zero value which will trigger
// an immediate backup) // an immediate backup)
lastBackupTime := schedule.Status.LastBackup.Time var lastBackupTime time.Time
if schedule.Status.LastBackup != nil {
lastBackupTime = schedule.Status.LastBackup.Time
}
nextRunTime := cronSchedule.Next(lastBackupTime) nextRunTime := cronSchedule.Next(lastBackupTime)

View File

@@ -174,7 +174,7 @@ func TestProcessSchedule(t *testing.T) {
t.Logf("error parsing status.lastBackup: %s\n", err) t.Logf("error parsing status.lastBackup: %s\n", err)
return false, nil, err return false, nil, err
} }
res.Status.LastBackup = metav1.Time{Time: parsed} res.Status.LastBackup = &metav1.Time{Time: parsed}
} }
return true, res, nil return true, res, nil
@@ -318,14 +318,21 @@ func TestGetNextRunTime(t *testing.T) {
offsetDuration, err := time.ParseDuration(test.lastRanOffset) offsetDuration, err := time.ParseDuration(test.lastRanOffset)
require.NoError(t, err, "unable to parse test.lastRanOffset: %v", err) require.NoError(t, err, "unable to parse test.lastRanOffset: %v", err)
test.schedule.Status.LastBackup = metav1.Time{Time: testClock.Now().Add(-offsetDuration)} test.schedule.Status.LastBackup = &metav1.Time{Time: testClock.Now().Add(-offsetDuration)}
} }
nextRunTimeOffset, err := time.ParseDuration(test.expectedNextRunTimeOffset) nextRunTimeOffset, err := time.ParseDuration(test.expectedNextRunTimeOffset)
if err != nil { if err != nil {
panic(err) panic(err)
} }
expectedNextRunTime := test.schedule.Status.LastBackup.Add(nextRunTimeOffset)
// calculate expected next run time (if the schedule hasn't run yet, this
// will be the zero value which will trigger an immediate backup)
var baseTime time.Time
if test.lastRanOffset != "" {
baseTime = test.schedule.Status.LastBackup.Time
}
expectedNextRunTime := baseTime.Add(nextRunTimeOffset)
due, nextRunTime := getNextRunTime(test.schedule, cronSchedule, testClock.Now()) due, nextRunTime := getNextRunTime(test.schedule, cronSchedule, testClock.Now())
@@ -371,7 +378,7 @@ func TestParseCronSchedule(t *testing.T) {
assert.Equal(t, time.Date(2017, 8, 11, 9, 0, 0, 0, time.UTC), next) assert.Equal(t, time.Date(2017, 8, 11, 9, 0, 0, 0, time.UTC), next)
// record backup time // record backup time
s.Status.LastBackup = metav1.NewTime(now) s.Status.LastBackup = &metav1.Time{Time: now}
// advance clock 1 minute, make sure we're not due and next backup is tomorrow at 9am // advance clock 1 minute, make sure we're not due and next backup is tomorrow at 9am
now = time.Date(2017, 8, 11, 9, 2, 0, 0, time.UTC) now = time.Date(2017, 8, 11, 9, 2, 0, 0, time.UTC)

View File

@@ -23,6 +23,7 @@ import (
jsonpatch "github.com/evanphx/json-patch" jsonpatch "github.com/evanphx/json-patch"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/clock" "k8s.io/apimachinery/pkg/util/clock"
@@ -47,7 +48,7 @@ func Process(req *velerov1api.ServerStatusRequest, client velerov1client.ServerS
log.Info("Processing new ServerStatusRequest") log.Info("Processing new ServerStatusRequest")
return errors.WithStack(patch(client, req, func(req *velerov1api.ServerStatusRequest) { return errors.WithStack(patch(client, req, func(req *velerov1api.ServerStatusRequest) {
req.Status.ServerVersion = buildinfo.Version req.Status.ServerVersion = buildinfo.Version
req.Status.ProcessedTimestamp.Time = clock.Now() req.Status.ProcessedTimestamp = &metav1.Time{Time: clock.Now()}
req.Status.Phase = velerov1api.ServerStatusRequestPhaseProcessed req.Status.Phase = velerov1api.ServerStatusRequestPhaseProcessed
req.Status.Plugins = plugins(pluginLister) req.Status.Plugins = plugins(pluginLister)
})) }))