From 4b0f654a1e322b5f76533fbf28c456934a95bde1 Mon Sep 17 00:00:00 2001 From: Steve Kriss Date: Wed, 22 Apr 2020 12:14:09 -0600 Subject: [PATCH] add progress info to backup status Signed-off-by: Steve Kriss --- pkg/apis/velero/v1/backup.go | 20 +++++ pkg/apis/velero/v1/zz_generated.deepcopy.go | 21 +++++ pkg/backup/backup.go | 83 ++++++++++++++++++- pkg/backup/backup_test.go | 1 + pkg/cmd/server/server.go | 1 + pkg/cmd/util/output/backup_describer.go | 12 +++ pkg/generated/crds/crds.go | 2 +- .../crds/manifests/velero.io_backups.yaml | 17 ++++ 8 files changed, 155 insertions(+), 2 deletions(-) diff --git a/pkg/apis/velero/v1/backup.go b/pkg/apis/velero/v1/backup.go index 6c978cef9..cabc84170 100644 --- a/pkg/apis/velero/v1/backup.go +++ b/pkg/apis/velero/v1/backup.go @@ -273,6 +273,26 @@ type BackupStatus struct { // file in object storage. // +optional Errors int `json:"errors,omitempty"` + + // Progress contains information about the backup's execution progress. Note + // that this information is best-effort only -- if Velero fails to update it + // during a backup for any reason, it may be inaccurate/stale. + Progress *BackupProgress `json:"progress,omitempty"` +} + +// BackupProgress stores information about the progress of a Backup's execution. +type BackupProgress struct { + // TotalItems is the total number of items to be backed up. This number may change + // throughout the execution of the backup due to plugins that return additional related + // items to back up, the velero.io/exclude-from-backup label, and various other + // filters that happen as items are processed. + // +optional + TotalItems int `json:"totalItems,omitempty"` + + // ItemsBackedUp is the number of items that have actually been written to the + // backup tarball so far. + // +optional + ItemsBackedUp int `json:"itemsBackedUp,omitempty"` } // +genclient diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index e7f1aeaec..d8e111b71 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -109,6 +109,22 @@ func (in *BackupList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupProgress) DeepCopyInto(out *BackupProgress) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupProgress. +func (in *BackupProgress) DeepCopy() *BackupProgress { + if in == nil { + return nil + } + out := new(BackupProgress) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BackupResourceHook) DeepCopyInto(out *BackupResourceHook) { *out = *in @@ -263,6 +279,11 @@ func (in *BackupStatus) DeepCopyInto(out *BackupStatus) { in, out := &in.CompletionTimestamp, &out.CompletionTimestamp *out = (*in).DeepCopy() } + if in.Progress != nil { + in, out := &in.Progress, &out.Progress + *out = new(BackupProgress) + **out = **in + } return } diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index cef92bfb4..102d3ad0c 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -35,11 +35,13 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" kubeerrs "k8s.io/apimachinery/pkg/util/errors" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/client" "github.com/vmware-tanzu/velero/pkg/discovery" + velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/podexec" @@ -63,6 +65,7 @@ type Backupper interface { // kubernetesBackupper implements Backupper. type kubernetesBackupper struct { + backupClient velerov1client.BackupsGetter dynamicFactory client.DynamicFactory discoveryHelper discovery.Helper podCommandExecutor podexec.PodCommandExecutor @@ -94,6 +97,7 @@ func cohabitatingResources() map[string]*cohabitatingResource { // NewKubernetesBackupper creates a new kubernetesBackupper. func NewKubernetesBackupper( + backupClient velerov1client.BackupsGetter, discoveryHelper discovery.Helper, dynamicFactory client.DynamicFactory, podCommandExecutor podexec.PodCommandExecutor, @@ -101,6 +105,7 @@ func NewKubernetesBackupper( resticTimeout time.Duration, ) (Backupper, error) { return &kubernetesBackupper{ + backupClient: backupClient, discoveryHelper: discoveryHelper, dynamicFactory: dynamicFactory, podCommandExecutor: podCommandExecutor, @@ -289,6 +294,11 @@ func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Req items := collector.getAllItems() log.WithField("progress", "").Infof("Collected %d items matching the backup spec from the Kubernetes API (actual number of items backed up may be more or less depending on velero.io/exclude-from-backup annotation, plugins returning additional related items to back up, etc.)", len(items)) + patch := fmt.Sprintf(`{"status":{"progress":{"totalItems":%d}}}`, len(items)) + if _, err := kb.backupClient.Backups(backupRequest.Namespace).Patch(backupRequest.Name, types.MergePatchType, []byte(patch)); err != nil { + log.WithError(errors.WithStack((err))).Warn("Got error trying to update backup's status.progress.totalItems") + } + itemBackupper := &itemBackupper{ backupRequest: backupRequest, tarWriter: tw, @@ -302,7 +312,51 @@ func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Req }, } + // helper struct to send current progress between the main + // backup loop and the gouroutine that periodically patches + // the backup CR with progress updates + type progressUpdate struct { + totalItems, itemsBackedUp int + } + + // the main backup process will send on this channel once + // for every item it processes. + update := make(chan progressUpdate) + + // the main backup process will send on this channel when + // it's done sending progress updates + quit := make(chan struct{}) + + // This is the progress updater goroutine that receives + // progress updates on the 'update' channel. It patches + // the backup CR with progress updates at most every second, + // but it will not issue a patch if it hasn't received a new + // update since the previous patch. This goroutine exits + // when it receives on the 'quit' channel. + go func() { + ticker := time.NewTicker(1 * time.Second) + var lastUpdate *progressUpdate + for { + select { + case <-quit: + ticker.Stop() + return + case val := <-update: + lastUpdate = &val + case <-ticker.C: + if lastUpdate != nil { + patch := fmt.Sprintf(`{"status":{"progress":{"totalItems":%d,"itemsBackedUp":%d}}}`, lastUpdate.totalItems, lastUpdate.itemsBackedUp) + if _, err := kb.backupClient.Backups(backupRequest.Namespace).Patch(backupRequest.Name, types.MergePatchType, []byte(patch)); err != nil { + log.WithError(errors.WithStack((err))).Warn("Got error trying to update backup's status.progress") + } + lastUpdate = nil + } + } + } + }() + backedUpGroupResources := map[schema.GroupResource]bool{} + totalItems := len(items) for i, item := range items { log.WithFields(map[string]interface{}{ @@ -310,7 +364,7 @@ func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Req "resource": item.groupResource.String(), "namespace": item.namespace, "name": item.name, - }).Infof("Processing item %d of %d", i+1, len(items)) + }).Infof("Processing item") // use an anonymous func so we can defer-close/remove the file // as soon as we're done with it @@ -334,8 +388,28 @@ func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Req backedUpGroupResources[item.groupResource] = true } }() + + // updated total is computed as "how many items we've backed up so far, plus + // how many items we know of that are remaining" + totalItems = len(backupRequest.BackedUpItems) + (len(items) - (i + 1)) + + // send a progress update + update <- progressUpdate{ + totalItems: totalItems, + itemsBackedUp: len(backupRequest.BackedUpItems), + } + + log.WithFields(map[string]interface{}{ + "progress": "", + "resource": item.groupResource.String(), + "namespace": item.namespace, + "name": item.name, + }).Infof("Backed up %d items out of an estimated total of %d (estimate will change throughout the backup)", len(backupRequest.BackedUpItems), totalItems) } + // no more progress updates will be sent on the 'update' channel + quit <- struct{}{} + // back up CRD for resource if found. We should only need to do this if we've backed up at least // one item for the resource and IncludeClusterResources is nil. If IncludeClusterResources is false // we don't want to back it up, and if it's true it will already be included. @@ -345,6 +419,13 @@ func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Req } } + // do a final update on progress since we may have just added some CRDs and may not have updated + // for the last few processed items. + patch = fmt.Sprintf(`{"status":{"progress":{"totalItems":%d,"itemsBackedUp":%d}}}`, len(backupRequest.BackedUpItems), len(backupRequest.BackedUpItems)) + if _, err := kb.backupClient.Backups(backupRequest.Namespace).Patch(backupRequest.Name, types.MergePatchType, []byte(patch)); err != nil { + log.WithError(errors.WithStack((err))).Warn("Got error trying to update backup's status.progress") + } + log.WithField("progress", "").Infof("Backed up a total of %d items", len(backupRequest.BackedUpItems)) return nil diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index c140295be..40e195ba4 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -2690,6 +2690,7 @@ func newHarness(t *testing.T) *harness { return &harness{ APIServer: apiServer, backupper: &kubernetesBackupper{ + backupClient: apiServer.VeleroClient.VeleroV1(), dynamicFactory: client.NewDynamicFactory(apiServer.DynamicClient), discoveryHelper: discoveryHelper, diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 1d3be4b4e..6ac9e3304 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -630,6 +630,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string backupControllerRunInfo := func() controllerRunInfo { backupper, err := backup.NewKubernetesBackupper( + s.veleroClient.VeleroV1(), s.discoveryHelper, client.NewDynamicFactory(s.dynamicClient), podexec.NewPodCommandExecutor(s.kubeClientConfig, s.kubeClient.CoreV1().RESTClient()), diff --git a/pkg/cmd/util/output/backup_describer.go b/pkg/cmd/util/output/backup_describer.go index 137fd3819..17952cf09 100644 --- a/pkg/cmd/util/output/backup_describer.go +++ b/pkg/cmd/util/output/backup_describer.go @@ -238,6 +238,18 @@ func DescribeBackupStatus(d *Describer, backup *velerov1api.Backup, details bool d.Printf("Expiration:\t%s\n", status.Expiration) d.Println() + if backup.Status.Progress != nil { + if backup.Status.Phase == velerov1api.BackupPhaseInProgress { + d.Printf("Estimated total items to be backed up:\t%d\n", backup.Status.Progress.TotalItems) + d.Printf("Items backed up so far:\t%d\n", backup.Status.Progress.ItemsBackedUp) + } else { + d.Printf("Total items to be backed up:\t%d\n", backup.Status.Progress.TotalItems) + d.Printf("Items backed up:\t%d\n", backup.Status.Progress.ItemsBackedUp) + } + + d.Println() + } + if details { describeBackupResourceList(d, backup, veleroClient, insecureSkipTLSVerify, caCertPath) d.Println() diff --git a/pkg/generated/crds/crds.go b/pkg/generated/crds/crds.go index 940b92ef8..9badb1f89 100644 --- a/pkg/generated/crds/crds.go +++ b/pkg/generated/crds/crds.go @@ -29,7 +29,7 @@ import ( ) var rawCRDs = [][]byte{ - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec;\fJ\x96a~\x06p`\x82\xe7D\x9d\xc7I\x95(?\xdc\xdd>\xfe\xef}\xb6ǂ\xf9N\x80\x1cM\xa6yI\xe3\x02r\xc0\r0x$\xd2@\a\x11\x80\xdd3\xeb\xfe#T\xa45`\xf7\b\x19+m\xa5\x11\xd4\x16~\xa86\xa8%Z4\x012@&*cQ\x83\xb1\xcc\"0\v\fJť\x05.\xc1\xf2\x02\xe1\x9b\x0fw\xb7\xa06?cf\r0\x99\x033Fe\x9cY\xcc\xe1\xa0DU\xa0\x9f\xfb\xed:\xc0,\xb5*Q[\x1e\x19\xedZK\xb3\xea\xbe\x1e]\x17\x8ep?\x06r\xa7K\xe8\xd1\x0f\x1a\x819\x18b\x8a\xa3\xc3\xee\xb9\x01\x8d\x81Lb`\v,\xb8!L\x06\xa4\xd7p濫\r\x98\xbd\xaaD\xee\x14\xf0\x80\xda\xf1)S;\xc9\xffYC6`\x15-)\x98\xc5 \xfbظ\xb4\xa8%\x13Nd\x15^\x12#\nv\x04\x8dn\r\xa8d\v\x1a\r1k\xf8\xb3\xd2\b\\n\xd5\x15\xec\xad-\xcdջw;n\xa3-e\xaa(*\xc9\xed\xf1\x1dY\x04\xdfTVi\xf3.\xc7\x03\x8aw\x86\xefVLg{n1s\xc2{\xc7J\xbe\"\xc4%\x99Һ\xc8\xff+J\xdd\\\xb40\xb5G\xa7d\xc6j.wu7\xa9\xfa$ߝ\xce{u\xf2\xd3<\xfe\r{]\x97\xe3\xcaO7\xf7\x0fmU\xe3\xa6\xcbs\xe2v3\xcd4\x8cw\x8c\xe2r\x8b\xda\vn\xabUA\x10Q\xe6^\xd7HM\x05G\xd9e\xba\xa96\x05\xb7N\xd2\xff\xa8\xd08uVk\xb8&\x8f\x02\x1b\x84\xaa̝\x16\xae\xe1V\xc25+P\\3\x83\xbf:\xdb\x1d\x87\xcdʱt\x99\xf1mG\xd8\x1d\xe8\xb9UwG\x975*!o\xf1\xf7%f\x1d\xc3ps\xf8\x96g\xa4\xfe\xb0U\xbaq\b\xde'\xad[\x00nj\xd25|\xc9D\x95c^\xbb\xa5\xde\xef=Tn\x06\xc3ɟ3.\x9d\xfe8\x0f\xealO6\xbf\x92Gb\x1a{@\x01\x9c\f\xb9\xf4\xd0\xc8\xd7\xecq\x04m2=\x8b\xc5\x00\xab\t\x86\aؕ\x10l#\xf0\n\xac\xae\xfaK\xfbyLkv\x1c\xe5D\xdc\xcf\xd2\x18Q\x8f\x0e\x16$xF\x9e\xb6\xb6\x13\xe2\xc5\xef\x88\r{\xa5\x9e\xe6I\xff\x93\x1b\xd1\xd89d\x14\x06\xc0\x06\xf7\xec\xc0\x95\x0e\xc4\x06g\xbbA\xc0\x17\xcc*K\xfb]\xb71\v9\xdfnQ;(\xe5\x9e\x194\u07b9O\xb1`J\x89]\xd3Sb\x1b\xe0߈\x8ci\xf4\xf4N\xa1\f\xcf{\x94\x84̐\xbb\xbe\xb9\x9dX\xe6\xfc\xc0\xf3\x8a\t\xe0\xd2X&3O\a\xabq\xea\xd3\x01\xd3\xe2\x1c`\xeb\x8d?\xe2\xecx\xdfq\x04J\"(\r\x85\xf3yáf\x14>L\x92\xbba\x06sP^\ru%Є\x85r\xf2/\x8d]_N\x00\xae\xa5\xe0wH\xc16(\xc0\xa0\xc0\xcc*=Ɔy\xa1\xfa\xb6\xec\xa3&x7⭂\xd3\f.\xb4\xed\xa8\xd4$L\x80\xe7=\xcf\xf6~\xf3r\xfaBP Wh\xc8~YY\x8a\xe38q0/i\xdffL\xb8i\xb3\xc6܇54\xeb\xa6-\xfa\xb9\xa6-x\xbc./k\xd1\xffqX\x19\x1d\xf7Ɋy;\x98\xf8\x9a\x8a\xe9\x98\xc8]\x00z\xbb\x05,J{\xbc\x04nc\xaf\x8bq\x19\x1d\x9f&\xd9S\xaf\xfd\xbb\x13ĩ:}۟\xf7\x8a:\xfd\v\xa5P/\xfd\xbb\x11\x029\xfb\xfb\xe0\xeb\x13\x05\xf0\xa9=\xe7\x12\xf8\xb6\x16@~\t[.,\xea\x9e$\xe6\xc8U\xf3\x92\xf8\xa5,Xީ\\+\x98\xcd\xf67/.:2M\xd6#\x89\x1b\xfd\xa9>\xa6\x8cQuw3\x9d\x85\ntd\xe2\x1a\v\u007f\x10{ \x0e6=\x14\xf9|\xf8\xfc\x11\xf3i\xa6@\x8a\x86\rH\xf8\xd0C\xb3\xbdl\b\x91\xd3\b\bAJ}\xba\xf0\x87\xeaK`\xf0\x84G\x1f]\xb8#~\x89\x9a\xb9e\xdc\xe0E\x88\x1a\xe9dO\n\xf5\x84G\x02\x12\x0e\xeb\vs\xd3D\xef\xdb\x13\x1e\x97\a\xf5\xd8\xe6\xb0\xe1&$\x1f\x1c\xff\\\a1\x80Nz\xa9,\x03J\xb5D\x0f\xb3D\x14\xa4\xba\x88\xd8\"\xb7O&\xaf\x16S\x93\x1d\xf0\x82\xbc0^(N\xdb\xf7\xbcL\"йN0H6\x11S-\x8fL\xf0\xbc^\xc6\xeb\xf7\xad\xbc\x84\xcf\xca\xdeʩ`\xb5\xdbn^\xb8\t\x19\xae\x8f\n\xcdge\xa9\xe7ՙ\xe8Q>\x99\x85~\x1a\x99\x90\xf4n\xd8\xd1\xdf\xce\xd8,*\xb1o\xb7\xfe\x84U\x8b\x84\x1b\xb8\x95\xee\f\xe1y\xe5sn~\xb19o\xdfmEe(%#\x95\\\xd1f\xb7\x1e['\xb08Q\x91\xdbR\x18\xa2U/\xe9\x97K\x82\xf8\xe0\xf6\x05?\xdb\xe7\x0f\x05\xcb0\x87\xbc\"&R\xfe\x8bY\xdc\xf1\f\nԻ鍠\xddJ\xe7\xb3S\x96O\U000a5f9d\xa4O)[sl\xc1\x19\xe7Kh\xac\x9cm.\x8e\x89\xa2]\x188\x9a\xf0\x9a\x1e\xb8D\am\x92\x147,p\x93\xe59]G0q\x97콓9?ܷ=J~\x8f+X\xe9\xac\xf3_n\xab\"\xa5\xfd7\x94\x8c\xebE\v\xfd@\xf7\n\x02;3CV\xa8\xbd\x88\x83\xcf\r8i\x1e\x98\xe8\xa7MG\xc8R\xcek\xa0\xf0۰\xda\x0e\"\x8dKx\xde+\xe3w\xc5-G\x91\x03\x9f\x8b\xb4\\;\u007f\xc2\xe3\xf9\xe5\xc0\xc6\xcfo\xe5\xb9ߞ\a\x16\x1b\xf7\xf2\x05\xc0J\x8a#\x9c\xd3\xcc\xf3/\x0f]\x92\xb4.a\x10]2\xa5\x05\xb3\xee4\x17wq7\xad\xbe\xa9p\xa1\xe84\xb6\t:W*c\x13\x91\xb8S\xc6\xfa\f]'x\x1c\xc9\r͟iBN\b\xd8\xd6\xdf\x0e)\x1d\xef\x01\x9c#\xeb\xa5*\x9d\x94\f\x8e&8\a\x10\xf3\x00\x92\t\x01獍z\xffx\xee/\ah\t\x96QX0\x03ѩB\xa9U\x86\xc6̩â\xe7]H\xb8\xd5\xc96\xe6\x0f\x15>\xd5>\x97܋-5lt\xac9)̾yi\xe5\x00\x9di\xbb\xff\xe7\xd5\xec4\x8c\x80nj\x8b\x82\xc9\xc5\xcdb\x80ܵ\x9f\x17M!\x80\xf1!\xbb\xdeUdƩ\x91^P\x9a\xaf\xbb\xc1\x16\\\xde\x12px\xff\xaa\xdb1D\x97\x88\xa7\x87\xd4\xd7qf\xc3\xe6\xba\xc3\xdbf\xa9\x86)\xf7\xb1\xf6\xbcG\x8d\x1dI\r3\xc3\x14\xceIe[\xc7\xf34F{<.\fl\xb96\xb6\x8d\xa4\x81j\xd6j[@N:\xa3\xc8\x1b\xad\xbf\xe0\x88\xf2\xa3\x9f\xd7J\x00\xed\xd5s\xbcO\xf3\fI\xa2\x98\xaeA\x10\xf8\x16\xb8\x05\x94\x99\xaa$%1\x9c\x91\xd2\x02\x9e\xa5ޙ.n\xb2\xbe\xa5\x18\xb6k(\xab\"\x85\xf0\x15i\x0f\x973\xb9\x8e\xf6\xe0\xef\x19\x9f\xcbT\xc5v\x92\x98,/PU3\x9bZ\xd3:bz\xf0\xf3:\x17\xa1\x05{\xe1EU\x00+\x1c\xb3\x938\xeavf^`W\xbe\xf0̸%\xef\ue812\xab\xb7\xca\x19E)Ц\x9d\x066\xb8U\x9al\xd1\xf0\x1c\xeb-3\xc8\\I`\xb0e\\T:ɣ\x9d\xc0\xd1\xf4\xc8>\x18\xf9\xeb\x04\xed)ˮ\x88\xfc\xc54eR\xa86\xe7UK\x9d\x1a\xa8\xddi|\xcd\x10\xa9\xd4\xdc\xe9\x8cz\xdd()\xa8\x12\x93Ƿ0\xa9ś\xb70i\xd0\xde¤N{\v\x93\xde¤\xd9\xf6\x16&\xbd\x85I\u007f\xd40i\x1e\x93\x15\xe5\xadF\u007fZX}\xf1\nu\x1a\xb1I\xc8\xe1V\xff\xdaW(\xa7\xd5\xe5ݎ\xcfi\xf9\xab\xe7=\xda=\xeaX\xf8\xbc\xa2\xba졜\x9b\xab\xff\xc6\xcdׅzN\xf9\xa3\xf2\xfa\xf2\xcb\xd9ҽ\x84B\xbc\x8dR\x02\x99\x1c\xa3?\xb56s\xa4\xa8\xa4[\x93X\x17vĢD\x15\x97\x18P\x1f\xeb\xbd]\x98ٮ``B\xb4kS\x98n\x98\xf2\x95\xea\x15\x17K?\x16\n>\xe6\xcb6\xa79\xd4\v\xed\xbb,ҝ\x12ï̡ٺ\x8c\xe9j\x8cp\x93\x81\x96\x1dޯ\xbb\xbfX\x15j3\xe0\x99\xdb\xfd\x80\x00*\x9atG\x16\xb9k\x17GF\x9d\nE\xf6}\u0381\xd2 \xb9\xb8\x1c\xad\x8b\xa9\xdf\x1f\xb4\xd9\t?\x96\xfePt\x92\xbdͅ\xf6)\xb5\x1b_\\\xb1ѭ\xc9\x18u\xb2\xa7]v\xa4\x96\x90\xa6\xd7dtk.&6\x99\x84J\x8c\x93+-\x96\xcf[\xb3U\x15_PK\x11\xeb$\xe66ܙ\n\x8a\x84\x98c\xb9Z\xe2\x8bj$\xe82o\x06\xeb\x93*#ZU\x0f3 \xd3\xea!\x12X\xb2T\xfbpr\xc5C\xbf\xca`\x86\x88\xa5:\x87\xe9\x1a\x86\x19\xa0\xa3\xd5\r)\x95\v30뚆W\xacWX\xa8Rx\x9dJ\xc2_\x1a{N\xd5\x1c,T\x1a,D\xa6sX-\xd4\x12\xa4W\x10,\xf0\xe7\v\xab\x05\xeaz\x80\xd15O\xad\x11\xe8V\x01\x8c\x82L\xac\f\x98\xb8\xfb\x1f\x05\x99P\x0f\xb0p\xe3?\nvvc\x9cшɟ\x8cd\xa5\xd9+\xfbHO\x1a\xe7\xa3\xc8\xfb\xeeؑÅ\x8bq\xd8\x13B&T\x95װ\x87\xa4\xd0C\xc5#\xdc=\x92\x93\xa7\xa70Y\xf3\x10(\xb8\xf2\x18\xfc\xf4\xdf\t}\xf7\x9a\x87\rc\x95f;\xfc\xa4\xb2\xd6{\xd4)\xfa\xbbc;\x8f\a\x83P\xe3\x91>\xd6A\xb0\xf8\x8a\xad;u,v\fY6\x1f\x1f\xb6N_\x0eá\xbc'-\xcfZ1K\xc4\xc3\xc3'\x8f\xb8\xe5\x05\xae?V\xfe \xb7*\x996\xe8\xf8\x17\t\xf2\x936\xeeϽz\x1e ,T\xa0\xf4\xbb>\xbe\x1a)\x87G\xa7\xc5d\xac\xfd\x8bڨ`\x91M\xf3\xea\xf88>\xa7\x15\x8b\xb6\x84\xe2\x0f6j;5k@`빯\x8b\xf6}E\xcbk=Q\x1bw\xce\xe3O$-\xb3\x95Yz$I\x83\xe2\x93\xe7\x90\xf1\xad4\xbd0\xf3\x00\xbc2\x9e\xfcN2\xa4\xb7:\xef\xd0\xe7dr=\x1cO\x0f\x8eu\ue462\xb4\x1a\x8bJ\xfe\xccL\x9d@\x1b\xf1h\r0?\x8f\x82\x01\a\vs\xc0\x03JP\x92\xf2e\xf4\x82˿\x86\xef\xcf\x19\x9e_[0B:\xae*\x85by\xb4܀Z|D\xfd@\xfeH\x1fP_\x98I\x88\x95\tɑ\x11\xf2\xfb\x9a\xb5U\xba`\xf6\nrfq5\x020\xc1\x8f\x8d\xa8\x14%\x8f\x17\x9en\xd2\x10o\x1d\x94w&\x95\x10\"$\x9e\v4\x86\xed\xe2\x9b\xcdg\xe7\x8ev(\xdd&7\x92%\n\xa1X\x93\xb8\xec\xbe_\xf4':\x96Yw\xfe\xf5\xa8\x85#lk\xd4\xc5\xd0\xe6\x84ڹ\x136\r\f廙\u007f\x1ew$\\Z\xdca7<\u0097\x92\xebe_~S\x0fs\x1c\xa1\xa3;Yx\xf3\x99\x01\x14|ǝCt\x82\xdd1\xbda;\\eJ\xb8s\x14W\xb2\x8fѯ#W\x0fu\xe4\x1b\x02\x03\x82\xbeo\x8f\x8c\a\xc1\xa0\xcc\x1eJ\xfc\xa4\xc0e\xd8Q\x9d\x04\v\xf6\xb3\xd2\xc3˚\x82K\xa5}\xf8B!t\x9c\x9a\xec\xcf\xe9Y\xeb,\xbewnD};\xd5\xf2U\x18\x95i|\x9f\x1f\xbb\xc5X\xc1g\xecoQ\xfeb\x02\xf3\xc7\xfa[\x13\x83\x01\xb7\xf2N\xab\x9d\x8b\xf8\x06?]G\xaf4\xf8\xe5\x8ei˙\x10G\x0f~b\xd5A\xf7Gt~ab#\x18a\xa0\xb1L\xdb4\x9f{\xdf\x19\xba\xe0n\t.\xe6k\xb8ǒ9\xe3\x1eȞ\xaeد\xfb_\x1f\xb9t!y\xfcԆ\xffRB\xb6grGoqA#E)\xfe\xc9\xcd\x00b\xc7\u007fv\xfce\x17\xf5\xdf\xc6U6\x1f\x1f\xb9Yv\x9a\x8f\xbd\xc1\xbd\xbc\xa9s\x9f\r\xbc\xe8\xea\xbe\xe1\xdba8Q\x96\x82g\x0e\xdbo\xbfR>\xf4\x90\xe0D.f\xfd\a9\x8b\xda\x15\xc0G,5fn\u007f\x18\"\u007f'Й\xb7A\xec:\xa6\x8bd?ލ\b\xcd\ak\xb1(G֚\t\t\x9bI\x91\x1e\xab,\x13 \xabb\x83\x9a\xe4\x17\a\f\b\x88\x9fx\x89\xa0\xc2\xc5\xdfd\f\x98LH\xedYN!\xa4\x9e4E\x88\xa9\xb2\f\x8d\xd9VB\f\x8f\xfbu\x88\xf5\x8aT=3\xed\xe2\xeay\xeb\xf9k\x184\x12t\x84\xf9\xaf\x1bv\xb4\xa2\x8e\x88\xdfo\x14w\x8c\x84\uef6e\xe6\vO\xef\x9b\xff\x88}\xab\xf0E\xa7\x83/\x1f!o\x99\xb7L;\xa0\x12z\x9a\xf3\x00\xcb2t\xba\xfb\xb9\xffq\xa7\xf3s\xfa'~\xbf\x89\xfe͔\xf4I\x1ds\x05\u007f\xfb\xfb\x19\x84c\xe5c\xc4\xc3u\xfe'\x00\x00\xff\xff\x03\xba\x12'\rK\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xaf?\xd5\r\u0087Qr\xb7\xc2a\x0e&\xa8\xa1\xad\x14\xba\xf8\xa2\x9c\xfdKc\xd7\xd7#\x80k)\x84\x1dR\x89-*p\xa80\xf3\xc6\x0e\xb1aZ\xa8a\xcc\xfb\xa8\x11\xde\rx\xab\xe84\xa3\vm;*3\n\x13\xe0\xf5 \xb3CؼH_\x18\n\xe4\x06\x1dۯ(K\xf5>L\x1cLK:\x8c\t\x13nƤ1\x9f\xc2\xea\x9bu3f\xfd\\3f<^\x97\x97\xb5\xe8\xff~X\x99\x1c\xf7ي\xb9\xe9-\xfcH\xc5$&J\n@7;\xc0\xa2\xf4\xef\xd7 }zJ1\xae\xe0\xf4i\x94=\xf5\xbb\u007fs\x828W\xa77\xa7\xeb>P\xa7\u007f\xa5\x14\xeaW\xfff\x84\xc0\xce\xfe1\xfa\xfa\x85\x02\xf8\xa5\xbd\xe6\x1a\xe4\xae\x16@~\r;\xa9<\xda\x13IL\x91k\xa6%\xf1kY0\xbfS\xd1(\x84\xcf\x0e\xf7o\x14\x1d\xb9\xa6걈\x1b\xa7KCL\x99\xa2\xea\xeef:\t\x158e\x92\x16\x8b\x90\x88=1\a\x9b'\x1c\xf9|\xfe\xfa\x05\xf3q\xa6\xc0\x12\r\xeb\x91\xf0\xf9\x04\xcd\xf6kc\x88\xbc\x8c\x80\x18\xa4\xd4\xd9EH\xaa\xafA\xc0\v\xbe\x87\xe8\x82R\xfc\x12\xad\xa0\xd7\xd0\xe4Y\x88\x169\xb3g\x85z\xc1w\x06\x12\x93\xf5\x99\xb5\xcbD\x1f\xc6\v\xbe\xcfO:a\x1ba#],>\x10\xff\xe8\x013\x803\xbd\xa5,\x03.\xb5$\x0f3G\x14,u\x11i$n\x9fM^-\xa6\xa6:\x10\x04y\xe5\x82PH\xdb\x0f\xb2\\D \xb9Np\xc86\x91J-\xcfBɼ~M\xd0\uf37e\x86\xaf\xc6o\xf4X\xb0\xda\x1d\xf7o\xd2\xc5\n\xd7\x17\x83\xee\xab\xf1\xfc\xe4Ù\x18P>\x9b\x85a\x19\x9b\x90\x0en\x98\xe8oWlf\x958\x8cMȰj\x91H\a\x1bM9D\xe0U\xa8\xb9\x85\x97My\xfb\xee(*\xc7%\x19m\xf4\x8a7\xbb\xf5\xd0{\"\x8b\x17*r[\n}\xb4\xeaW\x86\xd7-\x82\xf8D\xfbBX\x1d\xea\x87Jd\x98C^1\x13\xb9\xfe%<\xeee\x06\x05\xda\xfd\xf8F\xd0\x1e%\xf9\xec%\xaf_\xe4K\xc38K\x9f\x96l\xcdiDg\x9cϡ\xb1\"ۜ\x9d\x93D;3q\xb0\xe05>q\x8e\x0e\xde$9n\x98\xe1\xa6\xc8s>\x8e\x10\xeaa\xb1\xf7^\xcc\xf9\xfe\xbe\x1dP\n{\\!J\xb2\xce\xff\xa5\xad\x8a\x95\xf6\xff\xa0\x14\xd2\xceZ\xe8g>WP\xd8Y\x19\xabB\xed\x97\x10|逤y\x14\xea\xb4l:@\x96!\xaf\x81*l\xc3f\u05cb4\xae\xe1\xf5`\\\xd8\x15w\x12U\x0er*Ңq\xf9\x82\xef\x97\xd7=\x1b\xbf\xdc\xe8˰=\xf7,6\xed\xe53\x80\x8dV\xefp\xc9+/\xbf=tY\xa4u\v&\xf1!Ӳ`\x96\xb2\xb9\xb4\x8bӲ\xfa\xa4\x82B\xd1ql\x17\xe8\\i\x9c_\x88ăq>T\xe8:\xc1\xe3@mh:\xa7\x895!\x10\xbbp:dl:\a GvR\xaa$)9\x1c,p\xf6 \xe6\x11\xa4P\n.\x1b\x1b\r\xfe\xf12\x1c\x0e\xf0+D\xc6a\xc1\x04DR\x85Қ\f\x9d\x9bR\x87Y\xcf;Sp\xab\x8bm\"$\x15\xa1\xd4>U\xdcKci\xd8H\xac9+̾\u007fk\xd5\x00ɴ\xe9\xffi5;\x0f#\xe0\x93ڢ\x10zv\xb3\xe8!w\x17\xd6%S\x88`B\xc8n\xf7\x15\x9b\xf1\xd2H/*\xcd\xf7\xdd`\v\xa97\f\x1c>}\xe8v\f\xc9%\xe2\xf9!\xf5]Zٰ\xb9~\x10l\xb34\xfd\x92\xfb\xd0x=\xa0Ŏ\xa4\xfa\x95a\x0e\xe7\xb4\xf1\xad\xf4|\x19\xa3\x03\x1eW\x0ev\xd2:\xdfF\xd2A5i\xb5- g\xe5(\xfa\xde\xdaoHQ\xfe\x18ֵ\n@\a\xf3\x9a\xce\xd3\x02C\x16Q\xcc\xc7 \br\a\xd2\x03\xea\xccT\x9a\x8b\x18d\xa4\xfc\x82\xc0\xd2\xe0Lg7\xd90\x96\x186\r\xd4U\xb1\x84\xf0\x15k\x8f\xd4\x13\xb5\x8e\xf6䟅\x9c\xaaT\xa5q\x96\x98\xbc,\xd0T\x13\x9bZ3:bz\n\xeb:\a\xa1\x85x\x93EU\x80(\x88ً8J;\xb3,\xb0+_x\x15ҳw'\xa8\xec\xea\xbd!\xa3(\x15\xfae\xd9\xc0\x16wƲ-:\x99c\xbdeF\x99\x1b\r\x02vB\xaa\xca.\xf2hgptyd\x1f\x8d\xfcc\x82\xf6%\xaf]1\xf9\xb3e\xcaE\xa1ڔW-\xed\xd2@\xed\xc1\xe2G\x86H\xa5\x95\xa43\xe6c\xa3\xa4\xa8JB\xbf\xff\b\x93Z\xbc\xf9\x11&\xf5Ə0\xa93~\x84I?¤\xc9\xf1#L\xfa\x11&\xfd\xbd\x86IӘ\xac\xb8n5\xf8\xd3\xcc\xdbg\x8fP\xc7\x11\x1b\x85\x1cO\xf5\xefB\x87\U000b2fbc\xcd\U0001a5bfz=\xa0?\xa0M\x8d\xcf+\xee\xcb\xee˹9\xfao\xdc|ݨGʟ\x947\xb4_N\xb6\xee-h\xc4\xdb\x1a\xa3P\xe8!\xfa\x97\xf6f\x0e4\x95t{\x12\xebƎԔh\xd2+zԧ~o\n3\xdb\x1d\fB\xa9vo\x8a\xb0\rS\xbeS\xbf\xe2l\xeb\xc7L\xc3\xc7t\xdb\xe68\x87NB\xfb.\x8bl\xa7\xc5\xf0;sh\xb2/c\xbc\x1b#\x9ed\xa0\x17\xc7O\xeb\xee/\xde\xc4\xde\fx\x95\xfe\xd0#\x80\x9b&)e\xd1\xfbvsdҩ\xd8d\u007f\xca90\x16\xb4T׃}1\xf5\xfd\x836;\xe1\x8feH\x8aβ\xb7\xa9\xd0~I\xef\xc67wlt{2\x06\x9d\xecy\x87\x1dK[H\x97\xf7dt{.F6\x99\x05\x9d\x18gwZ\xcc\xe7[\x93]\x15\xdf\xd0K\x91\xfa$\xa66܉\x0e\x8a\x051\xc7|\xb7\xc47\xf5H\xf0a\xde\x04\xd6guF\xb4\xba\x1e&@.\xeb\x87X\xc0\x92\xb9އ\xb3;\x1eN\xbb\f&\x88\x98\xebs\x18\xefa\x98\x00:\xd8ݰ\xa4sa\x02f\xdd\xd3\xf0\x81\xfd\n3]\n\x1f\xd3I\xf8kcϱ\x9e\x83\x99N\x83\x99\xc8t\n\xab\x99^\x82\xe5\x1d\x043\xfc\xf9\xc6n\x81\xba\x1f`\xf0\x9d\xe7\xf6\bt\xbb\x00\x06A.\xec\f\x189\xfb\x1f\x04\xb9\xa0\x1f`\xe6\xc4\u007f\x10\xec\xe4\xc68\xa1\x11\xa3?9-Jw0\xfe\x99\xaf4NG\x91\x8fݹ\x03\xc9\x05\xc58\xe2\x05!S\xa6\xcak\xd8}R\xf8\xa2\xe2;<<\xb3\x93\xe7\xab0Ys\x11(\xba\xf2\x14\xfc\x9c\xde\x13\xfa\xe9#\x93\r\xe7\x8d\x15{\xfc\xc5d\xad\xfb\xa8c\xf4w\xe7v.\x0fF\xa1\xa6\x94>\xf5A\x88t\x8b\xad\xbbt(v\x8cU\xb6\x10\x1f\xb6\xb2/°/\xefQ\xcb\xf3^M\x12\xf1\xf4\xf4K@\xdc\xcb\x02\xd7_\xaa\x90ȭJa\x1d\x12\xff\x12Aaі\xfe<\x98\xd7\x1e\xc2\xcaDJ\u007f:\xc5\xd7\"\xd7\xf08[\\\x8cu\xb8Q\x9b\x14,\xb1iZ\x1d\x9f\x87״bіPBbcvc\xabz\x04\xb6\xae\xfbR\xb4\x1f:Z>\xea\x8aڰs\x1e\xbe\"酯\xdc\xdc%I\x9e\x94\xae<NJoe\xf9\x86Y\x00\x10\x94\xf1\xec{\x92\xb1\xbcչ\x87>%\x93\xbb\xfe|\xbepl\xf3\x80\x14\x97\xd5DR\xf2W\xe1\xea\x02ڀGk\x80\x85u\x1c\f\x10,\xcc\x01\x8f\xa8\xc1h\xae\x97\xf1\r\xaep\x1b\xfetM?\u007fm\xc1\x88帪TF\xe4\xc9r#j\xe9\x12\xf5\x13\xfb#{D{\xe5F!V.\x16G\x06\xc8?լ\x9d\xb1\x85\xf0\xb7\x90\v\x8f\xab\x01\x80\v\xfc\u0600Jq\xf1x\xe6\xea&O\t\xd6\xc1ugV\t\xa5b\xe1\xb9@\xe7\xc4>\xdd\xd9|%w\xb4GM\x9b\xdc@\x95(\x86bM\xe1\xb2{\u007f1dt\"\xf3\x94\xff\x06\xd4b\nۚuշ9e\xf6\x94a\xf3\xc4x\xaf:\xfa\xe7aG\"\xb5\xc7=v\xc3#|+\xa5\x9d\xf7\xe5\xf7\xf54\xe2\b\xa7\xeel\xe1\xcdg\x06Pɽ$\x87H\x82\xdd\v\xbb\x15{\\eFQ\x1e%\x8d>\xc5\xe8\xaf#\xd7\x00u\xe0\x1b\x02=\x82~n\xcfL\x89`T\xe6\x00%}R\xe0:\xee\xa8$\xc1B\xfc\xc5\xd8\xfeaM!\xb5\xb1!|\xe1\x10:-]\xec\xcf\xf9Z\xeb$\xbe\x0f4\xa3>\x9dj\xf9*L\xca4\xbc\xcf\x0f\x9db\xac\xe0+\x9enQ\xe1`\x02\xf3\xe7\xfa[\x13\xbd\t\x1b\xfd`͞\"\xbe\xdeOw\xc9+\xf5~y\x10\xd6K\xa1\xd4{\x00?\xf2\xd6\xde\xe3/H~ad#\x18b`\xc4l\x9a\x87qR\x13RJ\x1dd͇\t[S\xf9\x8e\xc15\x06ۓxzߚ\xf2DLu\x03مH; :\xbf\xc2\xdd\xceX\x1f\xe2\xd7\xd5\n\xe4.n,=\xa8䝹\xf2\x15\xbeY\x00\xd27Y\\\xa3\x9b\x1c\vZ\x14\x8eu\xd3\xf3w\x13\xb8\xec,\xb2\x8c\xe2\x13\xbcq^\xa8\x9e\x0f\x98*\x9e\xf0\x96L\n\x84\xf9\x9f{;V\x8f\x8f\x9b\xf6캕\xb5*\xb6hI\x19\x19X`\t\x9f\xde\x05Ǧ\x86\xb3\xb6-\xa2\x86W+\xbd'\x97Ү\xf9\x81''\xa2\x148\x03;1x-xܭ\xf1\xaf\xc6\v\xb5\x19\xcbY\xbbQ^=5\x91Ë\xfbD\x19\xe2\xf4\x96I\x1f$'\xf4oH\x97V\x92l\xb2\x83\xd0{\xd2\x11k\xaa\xfd!)\xd9\xc8f0\\\xa7\xab\b!(U\xb5'\xad\x8d\xb53_Y\xddJ>c5-o\xa1*\xb2\x17\xa8\xca\xe1\xc3\xe5\xf0є\xf8͛\x9bx\xddw\xb5\xb3\xa6XE\xfesY\xec:\x16\xe5\xac4\x14\x15q\xda\x12o܍\x80e\xb1\x97%j\x10.\xe22\xdbZ2%\xc8\xf1\\\xcc\v\xeb\x97\xc5Y\x8f\x9d\xa93!\x16\xc3\xc5|\r\x8fX\n\xb2\xa7\xbe\x9dZS\xc0\xdd\xe9\x17\x87\xae)\rO\x9f\xd7\t_G\t\xa2\xa7\xd4\x15,rf\x12\xae\xd9\xf5 vb\xa6N\x8c\xd4E\xfdo\x13\x1e5\x1f\x1c\xba\x9f\x0f\x94\x9eO&\x9f\x9c\x95\x90\x057\xf0Rx\xf3;\xb9\xeb\xa7\x10e\xa9dF\xd8\xfe\xfe;\x9d\x81\x1c\x17\x04\x0eW\x931\x03\a\b\xf5\xf6\x0f_\xb0\xb4\x98\x91U\xf6\x91\u007fPH[\xbaC\xec\x06#W\x8bc\xb7n\x16\xe8>{\x8fE9\xf0\xae\x894\xb0Y4\xe6\xf8D\x9a\xd0# }\xd6)\x81\x8a\x87\xfd\xa3y\xdfbB\xeah\xe2\x1cB\xeaEc\x84\xb8*#\a\xb4\xab\x86\xb6\xa2:\xad\xfa@\xaa^\x85\xa5\\z\xdaz\xfe#N\x1aH4\xe2\xfa\x8fM5Z\x99F\xc2\xefo\x94k\f\xf8\xf1\x93G\xcdW\xdd>5\xff1\xfbV\xf1+n\xc7\xd02\xc6\xde2o\x99vD%>ij\x00\"ːt\xf7\xeb\xe9\a\xdd./\xf9\x9f\xf4\xcd6\xfe73:\xec\xa5\xee\x16\xfe\xf3\xbf. \x96\x92\x9e\x13\x1e\xf4\xf0\xff\x03\x00\x00\xff\xff\xe9D\xe5\x86\x01O\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcX\xcdr\xdb6\x10\xbe\xeb)v܃/\x15\x95\xa4=txs\x94v&S'\xf1X\x89{h;\x13\bXJ\xa8A\x80\xc5\x02rԧ\xef,\bR\"E\xd9\xceLS\xde\b,\x17\xdf~\xfb\v\xce\xe6\xf3\xf9L4\xfa\x0e=igK\x10\x8d\xc6/\x01-\xbfQq\xff\x13\x15\xda-v/\xd7\x18\xc4\xcbٽ\xb6\xaa\x84e\xa4\xe0\xea[$\x17\xbd\xc47Xi\xab\x83vvVc\x10J\x04Q\xce\x00\x84\xb5.\b^&~\x05\x90\xce\x06\xef\x8cA?ߠ-\xee\xe3\x1a\xd7Q\x1b\x85>\x9dН\xbf{Q\xbc*~\x9c\x01H\x8f\xe9\xf3\x8f\xbaF\n\xa2nJ\xb0ј\x19\x80\x155\x96\xb0\x16\xf2>6\x14\x9c\x17\x1b4N\xb6g\x15;4\xe8]\xa1\u074c\x1a\x94|\xf4ƻؔp\xd8h5dX\xadI\xaf\x93\xb2U\xab\xec:+K\xfbFS\xf8\xf5\xbc̵\xa6\x90\xe4\x1a\x13\xbd0\xe7`%\x11\xd2v\x13\x8d\xf0g\x84f\x00\x8dGB\xbf\xc3O\xf6\u07ba\a\xfb\x8bF\xa3\xa8\x84J\x18\xc2\x19\x00I\xd7`\t\xef\x19}#$\xaa\x19\xc0N\x18\xad\xd2\xf7\xad=\xaeA{u\xf3\xf6\ue1d5\xdcb-\xdaE\x00\x85$\xbdn\x92ܴ%\xa0\t\x04t`\xe0a\x8b\x1e\xe1.\x91\x06\x8c\x14)\xc3\xce\x1a\x01\xdc\xfa/\x94\x81\x8a\xbc\xd0xנ\x0f\xbac\x96\x9f\xa3\xc8\xea\xd7F`.\x19m+\x03\x8ac\t\t\xc2\x16!G\x04*\xa0d\t\xb8\n\xc2V\x13xL4\xd9ppR\x8f\xa8\x02a3\xae\x02VL\xa5'\xa0\xad\x8bFq\x00\xee\xd0\a\xf0(\xdd\xc6\xea\u007fz\xcd\x04\xc1\xa5#\x8d\b\x98\xdd\xd9=\xda\x06\xf4V\x18\xe69\xe2\xf7 \xac\x82Z\xec\xc1#\x9f\x01\xd1\x1eiK\"T\xc0;\xe7\x11\xb4\xad\\\t\xdb\x10\x1a*\x17\x8b\x8d\x0e].IW\xd7\xd1\xea\xb0_\xa4\x8c\xd0\xeb\x18\x9c\xa7\x85\xc2\x1d\x9a\x05\xe9\xcd\\x\xb9\xd5\x01e\x88\x1e\x17\xa2\xd1\xf3\x04ܶ\xe1]\xab\xef|N<\xba\xa3\x03\xf1L\x94\xb6\x15\xfa\xd6q\x95wu҈V5Nې^\xa4\xd1h\x87\xa4S\\\xd7:\xb0\xa7\xff\x8eH\x81\xfdS\xc02U\x14X#\xc4F\x89\x80\xaa\x80\xb7\x16\x96\xa2F\xb3\x14\x84ߜvf\x98\xe6L\xe9\xd3\xc4\x1f\x17¡`\xcbV\xbf\xdcըI\x0fM\xa6\xe9\xaaA9\xc8\x13V\xa1+\x9dӶr\x1eDN\xdb\x01\xa7\x93ʊ#\x91\xa9\xf4M),%\x12\xbds\n\x87\xeb#\xb0W\xbd\xd8\x00]\x83\xbe֔\x9aI\xc2\xc6km\x19\x81\\\xfeFJ\xa1/A\xc5h\am\xac\xc7\x10\xe6p\x8bB}\xb0f?\xb9\xf1\x9b\xd7a|\xc0\xa4\xc3\xf8ia\xad\xf6Vޠ\xd7N=j\xee\xeb\x91po\xf4\xd6=@\x95\x02\xd7\x06\xb3\xe7\xcaB{+ǥ\xb3{\xaen\xdeve\xb4M\x8f\x9cM\x99\x9b\x02\xaerV\xba\n^\x80\xd2$\xd6\x06)\xa9\x1c\xd3\xc3͑wK\b>>\xdbh\xe9l\xa57cS\x85R\xa9\xa3\vss&*\x1eU:\xe2j\x99\xce\xe0R\xc3\x11\xd0x\xb7\xd3\n\xfd\xbc\v܌!\xfa\x1c\xc1\xa9鍭\x9b\xcc\x1e\xe8\xcbO\x8e\xebG]\xf6\xe1X\xb2\xefz\x19E\x97L\x18\xb8\xee\x11X\xe4p\x16~\x1cW\xc0\x1e\x95\xceZ\xf6Rp z{.i\xec\xbcѧ\xe7\x12\x8c\x9fu\x94\xf7\x18N\xd7\xc7Q\x97ĘɔG\xed[p\x10\t\x13\xb7\x8f\x03x\xc2g\x00R,\xd1?\x8dby\xc5b}\xc4\vX^\xc1:Ze\xb0\xc3\xf2\xb0E\xcb\r\\W{\xee\"\x1f\xafW\x13:\xa1\xe31\x15\x87܂;6\xa7\xb0W\xce\xd7\"\x94\xb0ޟ$\xf5\x93\xa65\x1e+\xfd\xe5I\xd3n\x92XGp#\xc2\x16\xb4%\xad\x10\xc4\x04\xdd\x13e\xb6{\xfa\x04\xfeд\x89\xf4\x95\xce\xe0\n\xa2=\x9e\x14\xa1y\x86\xf1\xdc\xf4\xe8\xf8|43n\xb2Pow\xf7\x9eF\xaeq\xc1\x9eN\xcd\x13+\xa6,\x98\x0f\xd3u\xb0\xd3\x1d\xfad\xcb\f\"D\xfaʦ\x99\xbeɂ\xeb\x9c\xeb2z\x8f6d\x85\xe0\xaaa\xdb\xebf\xdfo\xdd8/\x8e:'Oc\x16\xa2\x8d\x84\xaa-\x84\x05\xfca\xe1\r\xcfV\x92g\x9e\x92\x91\xf3\x98C'\xe1d\xdd\x03\u007f|\xa4-)\x00g\x93\xb5in\xe0\xe9\xb5\x1d\xc5\xd2փ6\x86\a*\x8f\xb5ۥ\xdb\xc4\xf0\xe1\xe9ǣك`z`\xf7\xaaxQ\\\xfc\xcf]\xd9\b\n\xdcfQ\xdd\xe2N\x8fo\x12\xa7l^\x9f\xc8wQ\xdd7R~\xf9܍h\v\x9f\xc5>\x9f\x98_i\xc3\x03\xe5D\n\x1c\xaeI\xed\xb5\x81\x02\x04]cz{\xbd\xba\xbe\xa4t\xdb\xe5a\xf8D\xe9\x03\xbb\x8f\x12@\xbe\\\xb8<\x03G\n\xe8'\x9c\xdd\xfbJ\x13X\a\xc6\xd9\xcd E\xda'O\xc4\xe0<\xb4\xa1\xe3<(\xe4a\x96˯\xdc\n\xbb\xc1\xc3-'c?BɁq\x8at\x18\x1d\x87h\xd0v:\x14\x9e\xe1C\xbe\xcc?\xea\xbf\xeb\x81h\xe7\xba!\xc3=\xea\xecKs|_\u007f6\xd7#鮹0\x91s>\xea?\x19\xaf\x9a\xad\xa0\xc7\r\xbea\x89\xce\xce\xe3\x92ԇ\xea\x93\x05\bΦ\xe1\xd5N\xe8\x84\xfad\xe7\x93\x15g\xf6\xce\xd82Q\x8bGK\x87_8/\x0fo\xa9&\xce\xf3/\x9b\xb4\x01\x90~q\xa8#\"sV\xe5\x95C\x81\xe7\n\xda\x04T\xefǿk..\x06\xff\\ҫt\xb6\x1dZ\xa9\x84\xdf\xff\x9c\xb5ZQ\xddu8x\xf1\xdf\x00\x00\x00\xff\xff\xc1e\xcb@\xee\x12\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96\xcdr$5\f\x80\xef\xfd\x14\xaa\xe5\xb0\x17\xa6\x87\x00\a\xaao\x90]\xaaR@*\x95,\xb9P\x1c<\xb6fZ\xc4m\x1bI\x9e\x10\x9e\x9e\xb2\xbb;\xf3\x93Ne9l\xdf,\xcb\xfa\xf9$\xb9ݬV\xab\xc6$\xbaG\x16\x8a\xa1\x03\x93\b\xffQ\fe%\xed\xc3\x0f\xd2R\\\xef/6\xa8\xe6\xa2y\xa0\xe0:\xb8̢q\xb8E\x89\x99-~\xc0-\x05R\x8a\xa1\x19P\x8d3j\xba\x06\xc0\x84\x10\xd5\x14\xb1\x94%\x80\x8dA9z\x8f\xbc\xdaah\x1f\xf2\x067\x99\xbcC\xae\x1ef\xff\xfbo\xdao\xdb\xef\x1b\x00\xcbX\x8f\u007f\xa2\x01E͐:\b\xd9\xfb\x06 \x98\x01;p\xe8Qqc\xecCN\x8c\u007fg\x14\x95v\x8f\x1e9\xb6\x14\x1bIh\x8b\xe3\x1dǜ:8l\x8c秠Ƅ>TS?US\xb7\xa3\xa9\xba\xebI\xf4\x97\xd74~\xa5I+\xf9\xcc\xc6/\aT\x15\x84\xc2.{Ë*\r@b\x14\xe4=\xfe\x1e\x1eB|\f?\x13z'\x1dl\x8d\x17l\x00\xc4Ƅ\x1d\\\x97\xa8\x93\xb1\xe8\x1a\x80\xbd\xf1\xe4*\x9e1\x8f\x980\xfcxsu\xffݝ\xedq0\xa3\x10\xc0\xa1X\xa6T\xf5\x96r\x00\x1200E\x02\x1a\xa7\x00!\x06\x84\xc80DF\x18\xa3\x95v2\x998&d\xa5\x99`\xf9\x8e\xfa\xe7Yv\xe6\xfc}\x89n\xd4\x01W:\x06\x05\xb4G\x98\xea\x8e\x0e\xa4F\x0eq\vړ\x00c\xc5\x12\xc6\x1e:2\vE\xc5\x04\x88\x9b\xbf\xd0j\vw\x05\x1d\vH\x1f\xb3w\xa5\xcd\xf6\xc8\n\x8c6\xee\x02\xfd\xfblYJ~ť7:\x17x\xfe((r0\xbep\xcd\xf85\x98\xe0`0O\xc0X|@\x0eG֪\x8a\xb4\xf0[\x81Ca\x1b;\xe8U\x93t\xeb\xf5\x8et\x9e\x18\x1b\x87!\aҧu\xed{\xdad\x8d,k\x87{\xf4k\xa1\xddʰ\xedI\xd1jf\\\x9bD\xab\x1ax\xa8\x03\xd3\x0e\xee+\x9e\xc6K\xde\x1fE\xaaO\xa5\x13D\x99\xc2\xeeY\\{\xf8U\xee\xa5\u007f\xc72\x8f\xc7\xc6\xf8\x0fx\x8b\xa8P\xb9\xfdx\xf7\tf\xa7\xb5\x04\xa7\xcc+\xed\xc319\x80/\xa0(l\x91\xc7\xc2m9\x0e\xd5\"\x06\x97\"\x05\xad\v\xeb\t\xc3)tɛ\x81T\xe6\xf6+\xf5i\xe1\xb2\xde\x1b\xb0A\xc8\xc9\x19E\xd7\xc2U\x80K3\xa0\xbf4\x82_\x1c{!,\xab\x82\xf4m\xf0\xc7\xd7ݩ\xe2H\xebY<\xdfE\x8b\x15Z\x18˻\x84\xb6Ԭ\x80+giK\xb6\x8e\x01l#\xc3cO\xb6\x9f\xc7\xf2\x84\xe8\xf3\x00\xb7G⥁-\xdfh\xa0\xdc*\xa7\xf2W\x92\x85Z'b<\xe9\xb5Ց\x997)\xa8\xd1,\xff\x8bC=1\x93\xb0\x99\x19\x83Nv\xea-\xb0t\xe8srG\xe6\xc8r\x9e\xf7I8\x1f\xabJ\xfdk\x19\n\x02&t\xb1\xb1\x16\x93\xa2\xbb>\u007fN\xbc{w\xf2.\xa8K\x1b\x83\xa3\xf15\x04\u007f\xfcٌV\xd1\xdd\xcfq\x14\xe1\u007f\x01\x00\x00\xff\xff\xcb0\x9b\f\x8c\t\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WOoܶ\x13\xbd\xebS\f\xf2;\xe4W \xd26m\x0f\x85n\xad\x93\x02A\xd3 \xb0\x13_\x8a\x1e\xb8\xe4\xacĚ\"Y\xcep\x1d\xf7\xd3\x17CI\xfbG+\xdb\xe9\xa1{\xd3p8|||oȭ꺮T\xb4\xb7\x98\xc8\x06߂\x8a\x16\xbf0z\xf9\xa2\xe6\xeeGjl\xd8\xec_o\x91\xd5\xeb\xea\xcez\xd3\xc2U&\x0e\xc35R\xc8I\xe3\x1b\xdcYo\xd9\x06_\r\xc8\xca(Vm\x05\xa0\xbc\x0f\xac$L\xf2\t\xa0\x83\xe7\x14\x9c\xc3Tw蛻\xbc\xc5m\xb6\xce`*+\xcc\xeb\xef\xbfm\xbek~\xa8\x00t\xc22\xfd\x93\x1d\x90X\r\xb1\x05\x9f\x9d\xab\x00\xbc\x1a\xb0\x05\x13\xee\xbd\v\xca$\xfc+#15{t\x98BcCE\x11\xb5,ڥ\x90c\vǁq\xee\x04h\xdc̛\xa9\xcc\xf5X\xa6\x8c8K\xfc\xeb\xda\xe8{;eD\x97\x93r\x97 \xca Y\xdfe\xa7\xd2\xc5p\x05\x10\x13\x12\xa6=~\xf6w>\xdc\xfb_,:C-\xec\x94#\xac\x00H\x87\x88-|\x10\x94Qi4\x15\xc0^9k\n\x15#\xee\x10\xd1\xff\xf4\xf1\xdd\xed\xf77\xba\xc7A\x8dA\x00\x83\xa4\x93\x8d%o\x89\x1b,\x81\x82\t\x05p8\x00\x03\xe5A%\xb6;\xa5\x19v)\f\xb0U\xfa.ǩ&@\xd8\xfe\x89\x9a\x818$\xd5\xe1+\xa0\xac{PRmL\x04\x17:\xd8Y\x87\xcd4%\xa6\x101\xb1\x9dY\x96߉\xbe\x0e\xb1\x05\xe0\x97\xb2\xa31\a\x8c(\n\t\xb8G\x98t\x81\x06\xa8\xec\x16\xc2\x0e\xb8\xb7\x04\t\v\x95~\xd4\xd8IY\x90\x14\xe5'\xe4\r\xdc\b݉\x80\xfa\x90\x9d\x11\x19\xee11$ԡ\xf3\xf6\xefCe\x12^dI\xa7x\x16\xc2\xfc\xb3\x9e1y\xe5\xe4,2\xbe\x02\xe5\r\f\xea\x01\x12\x16v\xb2?\xa9VR\xa8\x81\xdfBB\xb0~\x17Z\xe8\x99#\xb5\x9bMgyv\x94\x0eÐ\xbd\xe5\x87M\xf1\x85\xddf\x0e\x896\x06\xf7\xe86d\xbbZ%\xdd[F\xcd9\xe1FE[\x17\xe0\xbe\x18\xaa\x19\xcc\xff\xd2d?zy\x82\x94\x1fD=\xc4\xc9\xfa\xee\x10.:\u007f\x94w\xd1\xf9(\x8fqڈ\xffH\xaf\x84\x84\x95\xeb\xb77\x9f`^\xb4\x1c\xc19\xe7\xa3N\x0e\xd3\xe8H\xbc\x10e\xfd\x0e\xd3xpEeR\x11\xbd\x89\xc1z.\x1f\xdaY\xf4\xe7\xa4S\xde\x0e\x96i\x96\xad\x9cO\x03W\xa5\xaf\xc0\x16!G\xa3\x18M\x03\xef<\\\xa9\x01ݕ\"\xfc\xcfi\x17\x86\xa9\x16J\x9f'\xfe\xb4\x1d\x9e'\x8el\x1d\xc2s\xbfZ=\xa1\x85\x95o\"j9/!M\xe6ٝ\xd5\xc5\x02\xb0\v\t\xd4\xd1\xd9\x13m\xcdI\xdd5o\x16P*u\xc8\xe7\xb1\x05\x8aO%E\x16\xbe\xef\xd5y\v\xf9?6]#}\x80&\bcg\xf8\xa6Y\xd4{l\xf55\x8d\xaeb\x98\xa5*[\x17\x1e\xc5\xe8\xd2zN\xd1,\x17\x95\x1f\xfa<\xac\x15\xaf\xe1\xe7\x82\xf4}\xe8\x9e\x18\xbd\n\x9eE\xd0O\xa4\xdc\x06\x97\a\xbc\xf1*R\x1f\x9e̜/\xcd\xc3E\xb2L\xbbFi\xb5\xf8\x18\xa4i\xf8\x1a)\xbbՅV\x858\xff\xca\xc5\xf9\x1c\xcbr\xf7\xcc,˄\xb1\xe3\"ȅ\x9d<2ұ\r\xdc[\xeeᾷ\xba_\xa9\neZ9 \xe9/DA\xdb\xe2\xd8\u007f\a[tl\x13^ȣ.\xa2\xb9\b\n\xe4j\xad\xf8\xc2s\xeb\x85\xeb\xc9\v\xcf:\x96\x15g\xfajϖ\xec\x99T\x9dSB\xcfS\x8dr[-'|\x8dig\xc5\u007f\xbe~\xff\xa4s\xdf\x1c\xf3\xca\x1bLY?\xe2\x88\tk\xb2\x9dܭ2&\xde-\xceZ\x120\xfeN\xef\xf8gO\r\xbfD\x9bN\x9e,\x8f@{{H\x1b\x1b\v\xfa\xf1\x8aX\xbe^J9\xa4r\xedj\xe5/\xb0m\x11\f:d4\xb0}\x18;\xe3\x031\x0eK\xbc\xbb\x90\x06\xc5-\xc8\xc5Q\xb3\xbd\x10\x8a\xbc/\xd5\xd6a\v\x9c\xf2\xba\x8aV6\x1b{E\x17\xb6:\xdb\xe7G\xc9X;\xfe\x83\xb9\x9e8\u007fx\xa4\x83\xd5\xf0\x01\xef/b\x1fS\xd0H\x84Kc<\x82~E܋\xd0\xf1a\xfe\xfa\xf8U\xa4XO\x0f\xf12\x00P\x9e\xb5愺\xe9\xcd8E\x8e\x8eQZcd4\x1f\x96O\xf1\x17/\xce\xde\xd6\xe5S\ao\xec\xf8/\x02~\xff\xa3\x1a\xab\xa2\xb9\x9dqH\xf0\x9f\x00\x00\x00\xff\xff\xbbظ3\xc4\f\x00\x00"), diff --git a/pkg/generated/crds/manifests/velero.io_backups.yaml b/pkg/generated/crds/manifests/velero.io_backups.yaml index 2b0b34606..e638fe69b 100644 --- a/pkg/generated/crds/manifests/velero.io_backups.yaml +++ b/pkg/generated/crds/manifests/velero.io_backups.yaml @@ -355,6 +355,23 @@ spec: - Failed - Deleting type: string + progress: + description: Progress contains information about the backup's execution + progress. Note that this information is best-effort only -- if Velero + fails to update it during a backup for any reason, it may be inaccurate/stale. + properties: + itemsBackedUp: + description: ItemsBackedUp is the number of items that have actually + been written to the backup tarball so far. + type: integer + totalItems: + description: TotalItems is the total number of items to be backed + up. This number may change throughout the execution of the backup + due to plugins that return additional related items to back up, + the velero.io/exclude-from-backup label, and various other filters + that happen as items are processed. + type: integer + type: object startTimestamp: description: StartTimestamp records the time a backup was started. Separate from CreationTimestamp, since that value changes on restores. The