From 69cceb0d7eddc4aa06f53e1fc306ce9f6089d915 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Tue, 2 Jun 2020 17:33:08 -0700 Subject: [PATCH 01/10] add backup level flag to opt-in for default restic use Signed-off-by: Ashish Amarnath --- pkg/apis/velero/v1/backup.go | 6 ++++++ pkg/apis/velero/v1/zz_generated.deepcopy.go | 5 +++++ pkg/generated/crds/crds.go | 4 ++-- pkg/generated/crds/manifests/velero.io_backups.yaml | 4 ++++ pkg/generated/crds/manifests/velero.io_schedules.yaml | 4 ++++ 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/pkg/apis/velero/v1/backup.go b/pkg/apis/velero/v1/backup.go index 72aafc95e..d0875506c 100644 --- a/pkg/apis/velero/v1/backup.go +++ b/pkg/apis/velero/v1/backup.go @@ -82,6 +82,12 @@ type BackupSpec struct { // VolumeSnapshotLocations is a list containing names of VolumeSnapshotLocations associated with this backup. // +optional VolumeSnapshotLocations []string `json:"volumeSnapshotLocations,omitempty"` + + // DefaultRestic specifies whether restic should be used to take + // backup of all pod volumes by default. + // +optional + // + nullable + DefaultRestic *bool `json:"defaultRestic,omitempty"` } // BackupHooks contains custom behaviors that should be executed at different phases of the backup. diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index d8e111b71..bb22e0635 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -246,6 +246,11 @@ func (in *BackupSpec) DeepCopyInto(out *BackupSpec) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.DefaultRestic != nil { + in, out := &in.DefaultRestic, &out.DefaultRestic + *out = new(bool) + **out = **in + } return } diff --git a/pkg/generated/crds/crds.go b/pkg/generated/crds/crds.go index 11a95305e..c076ed81e 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\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\xf8\xe6r\x17\xefפ]\x98\xff\xb9\xb7\x9d\xf5\x98\xbciϮ\xfb\\\xabb\x8b\x964\x95\x81\x05~\xf1\xd1^\xf0zj8\xa5\xdb\"jx\xb5\xd2{\xf27\xed\x82 x\xf20J\x813\xb0\x13\x83w\x86\xc7}\x1e\xffj\xbcP\x9b\xb1\x84\xb6\x1b\x02\xd6S\x139\xbc\xb8O\x94!1l\x99\xf4ArBs\x87ti%\t.;\b\xbd'\x05\xb2\xa6\xda\x1f\x92\x06\x8e\xec\x14\xc3E\xbc\x8a\x10\x82RU{R\xe9XX\xf3\x95խ\xcc4\x96\xda\xf2\x16\xaa\"{\x81\xaa\x1c>y\x0e_T\x89\x1fĹ\x89w\x81W;k\x8aU\xe4?\xd7̮c\xc5\xceJC!\x13\xe74\xf1:\xde\bX\x16{Y\xa2\x06\xe1\".\xb3}'S\x82\x1cOԼ\xb0~Y\x10\xf6ؙ:\x13\u007f1\\\xcc\xd7\xf0\x88\xa5 c\xeb\x1b\xb15\x05ܝ~\x8e\xe8\x9ar\xf4\xf4\xed\x9d\xf0\xe9\x94 z\xcak\xc1\"\xa7-\xe1\x0e^\x0fb'\xa0\xea\x04P]\xd4\xff6\xb1S\xf35\xa2\xfb\xf9(\xea\xf9d\xf2\xc9A\nYp\x03/\xc5>\xbf\x93\xbb~~Q\x96Jf\x84\xed\xef\xbf\xd3\x01\xc9qATq5\x19Pp\xf4P\xc7\x06\xf0\x05K\x8b\x19Ye\x1f\xf9\a\x85\xb4\xdf;\xc4n\xa4r\xb58\xb0릈\xee\xb3\xf7X\x94\x03\xef\x9a\xc8\x11\x9bEc\x8eO\xa4\t=\x02\xd27\x9f\x12\xa8\xd8\t0\x9a\x14.&\xa4\x0e5\xce!\xa4^4F\x88\xab2r@\xbbjh+\xaas\xae\x0f\xa4\xeaUXJ\xb4\xa7\xad\xe7?⤁,$\xae\xff\xd8<\xa4\x95\x86$\xfc\xfeF\x89Ȁ\x1f?y\xd4|\xf2\xedS\xf3\x1f\xb3o\x15?\xf1v\f\xfdd\xec-\xf3\x96iGTⓦ@ \xb2\fIw\xbf\x9e~\xed\xed\xf2\x92\xffI\x1ft\xe3\u007f3\xa3\xc3^\xean\xe1?\xff\xeb\x02b\x9d\xe99\xe1A\x0f\xff?\x00\x00\xff\xffً\xab\xc5\x1eO\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xecp\x8e\x0e>$\xd9o\x98\xe1\xa6\xc8s\xbeT\x11\xeaa\xb1\xf5^\xcc\xf9\xfe\xb9\x1dP\ng\\!8A\xf7\xbftT\xb1\xd2\xfe\x1f\x94B\xda\xd9\x1d\xfa\x99oG\x14vfƬP{\x11\x82/\x1d\x904\x8fB\x9d&\u007f\a\xc82d5P\x85c\xd8\xecz\x9e\xc65\xbc\x1e\x8c\v\xa7\xe2N\xa2\xcaANyZ\xd4._\xf0\xfd\xf2\xba\xb7\xc7/7\xfa2\x1cϽ\x1d\x9b\xce\xf2\x19\xc0F\xabw\xb8䙗\xdf\xee\xba,Һ\x05\x83\xf8\xaal\x993K\xd1\\:\xc5iZ}\xdfB\xae\xe88\xb6\vt\xae4\xce/D\xe2\xc18\x1f2t\x1d\xe7q 74\x1d\xd3Ĝ\x10\x88]\xb8\xe326\xddf\x90!;IU\x92\x94\x1c\x0e&8{\x10\xf3\bR(\x05\x97\xcd\x1e\r\xf6\xf12\\q\xf0\x12\"c\xb7`\x02\"\xa9BiM\x86\xceM\xa9ì\xe5\x9dI\xb8\xd5\xc96\x11\x82\x8apa0\x95\xdcKm\xa9\xdbH\xac9\xcb;\u007fk\xe5\x00ik\xd3\xff\xd3jv\x1eF\xc0\xf7\xcdE!\xf4\xeca\xd1C\xee.\xccK[!\x82\t.\xbb\xddW\xbc\x8d\x97zzQi~\xdd\x03\xb6\x90z\xc3\xc0\xe1Ӈ\x1eǐL\"\x9e\xefRߥ\x99\r\x9b뎰7K\xd3O\xb9\x0f\xb5\xd7\x03Z\xecH\xaa\x9f\x19fwN\x1b\xdf\nϗ1:\xe0q\xe5`'\xad\xf3m$\x1d_s}|\x8c\xa2\xef\xad\xfd\x86\x10\xe5\xcfa^+\x01t0\xaf\xe9V00d\x11\xc5|\r\x82 w =\xa0\xceL\xa59\x89A\x9b\x94\x17\b,\r\xc6t\xf6\x90\rm\xc9Ʀ\x86\xba*\x96\x10\xbeb\xed\x91z\"\xd7\xd1\x1e\xfc\xa3\x90S\x99\xaa\xd4\xce\x12\x93\x97\x05\x9aj\xe2PkZGLOa^\xe7:\xb7\x10o\xb2\xa8\n\x10\x051{\x11G\xe9d\x96\x05v\xe5\v\xafBz\xb6\xee\x04\x95M\xbd7\xb4)J\x85~Y4\xb0ŝ\xb1\xbc\x17\x9ḏ>2\xa3̍\x06\x01;!Ue\x17Y\xb438\xbaܳ\x8f\x9b\xfcc\x9c\xf6%ˮ\x98\xfc\xd94\xe5\"Wmʪ\x96v\xa9\xa3\xf6`\xf1#]\xa4\xd2J\xd2\x19\xf3\xb1^RT%\xa1߿\xbbI-\xde|w\x93z\xed\xbb\x9b\xd4i\xdfݤ\xefn\xd2d\xfb\xee&}w\x93~\xafn\xd24&+\xce[\r\xfe4\xb3\xfa\xec\x15\xea8b\xa3\x90\xe3\xad\xfe]\xa8\xb3^V\x97\xb7\x19\x9e3Pu\x19˷W\\]ޗss\xf5ߘ\xf9\xbaP\x8f\x94?)o(\"\x9d,\xdd[P\x887T\x9b9_^2WTҭI\xac\v;RQ\xa2IK\xf4\xa8OU\xeb\xe4f\xb6+\x18\x84R\xed\xda\x14a\x1b\xa6\xfcJ\xf5\x8a\xb3\xa5\x1f3\x05\x1f\xd3e\x9b\xe3\x1c:q\xed\xbb,\xb2\x9d\x12\xc3_\x99C\x93u\x19\xe3\xd5\x18\xf1&\x03\xbd8~Zw\u007f\xf1&\xd6f\xc0\xab\xf4\x87\x1e\x01\\4I!\x8b\u07b7\x8b#\x93Nŧ\x02\xa7\x9c\x03cAKu=X\x17S\xbf\xa2h\xb3\x13\xfe\\\x86\xa0\xe8\xac\xfd6\xe5\xda/\xa9\xdd\xf8抍nMƠ\x91=\xef\xb2ci\t\xe9\xf2\x9a\x8cn\xcd\xc5\xc8!\xb3\xa0\x12\xe3\xecJ\x8b\xf9xk\xb2\xaa\xe2\x1bj)R\x9d\xc4ԁ;QA\xb1\xc0瘯\x96\xf8\xa6\x1a\t\xbe̛\xc0\xfa\xacʈV\xd5\xc3\x04\xc8e\xf5\x10\vX2W\xfbpv\xc5\xc3i\x95\xc1\x04\x11su\x0e\xe35\f\x13@\a\xab\x1b\x96T.L\xc0\xack\x1a>\xb0^a\xa6J\xe1c*\t\u007f\xa9\xef9Vs0Si0\xe3\x99Na5SK\xb0\xbc\x82`\x86?\xdfX-P\xd7\x03\f\xaeyn\x8d@\xb7\n`\x10\xe4\xc2ʀ\x91\xbb\xffA\x90\v\xea\x01fn\xfc\a\xc1N\x1e\x8c\x13\x1a1\xfa\x93Ӣt\a\xe3\x9f\xc3c\xaaI?\xe9\xb1;v \xb8 \x1fG\xbc d\xcaTy\r\xbbO\n?\xb7|\x87\x87g6\xf2\xfc\x14&k\x1e\x02ES\x9e\x9c\x9f\xd3wB?|d\xb0ἱb\x8f?\x99\xac\xf5\xaav\x8c\xfe\xee\xd8\xce\x13\xc8(\xd4\x14ҧ:\b\x91\xde\xe2u\xa7\x0e\xf9\x8e1\xcb\x16߹5\xd1\x17aؗ\xf7\xe8\xce\xf3^M\x12\xf1\xf4\xf4S@\xdc\xcb\x02\xd7_\xaa\x10ȭJa\x1d\x12\xff\x12AaҖ\xfe<\x98\xd7\x1e\xc2\xcaDJ\u007f8\xc5\xd7\"\xe7\xf08Z\\\x8cux˗\x14,\xb1iZ\x1d\x9f\x87\xe7\xb4|іPB`cvc\xb3z\x04\xb6\x1e-\x93\xb7\x1f*Z>\xea\x89ڰq\x1e~\xe8酯\xdc\xdcSO\x1e\x94\x1ennjoe\xf9\x85Y\x00\x10\x94\xf1\xecמ1\xbd\xd5yM?%\x93\xbb\xfex~6m\xf3\x80\x14\xa7\xd5DR\xf2W\xe1\xea\x04ڀEk\x80\x85y\xec\f\x10,\xcc\x01\x8f\xa8\xc1hΗ\xf1\v\xae\xf0\xa6\xfftN?~m\xc1\x88鸪TF\xe4i\xe7\xa6w\xa6\xf1)\xf8\x13\xdb#{D{\xe5F!\xf2\xc3՝\xb1C\xe4\x9fj\xd6\xce\xd8B\xf8[ȅ\xc7\xd5\x00\xc0\x05vl@\xa58y<\xf3t\x93\x87\x84\xdd\xc1y\xe7\xf4\x966$\x9e\vtN\xecӛ\xcdW2G{\xd4t\xc8\rd\x89\xa2+\xd6$.\xbb\xef\x17CD'2O\xf1o@-\x86\xb0\xadQW\xfd=\xa7̞\"l\x1e\x18_\x87G\xfbHN(\xee\x90.\xcd$\xc1e\a\xa1\xf7\xa4@\xd6T\xfbC\xd2\xc0\x91\x93b8\x89W\x11BP\xaajO*\x1d\x13k\xbe\xb2\xba\x15\x99\xc6T[\xdeBUd/P\x95\xc37\xcf\xe1\xbb0\xf1\xb3>7\xf1-\xf0jgM\xb1\x8a\xfc\xe7\x9c\xd9u\xcc\xd8Yi\xc8e\xe2\x98&>\xc7\x1b\x01\xcbb/K\xd4 \\\xc4e\xb6\xeedJ\x90ず\x17\xd6/s\xc2\x1e;Cg\xfc/\x86\x8b\xf9\x1a\x1e\xb1\x14\xb4\xd9\xfa\x9bؚ\x02\xeeN?\xaatM1z\xfa\x82P\xf8\x00L\x10=ŵ\xfcm\x0fc1\xbc\xc1\xebA\xec8T\x1d\a\xaa\x8b\xfa\xdf\xc7wj\xbe\xa9t?\xefE=\x9f\f>\xb9H\xa1\x1d\xdc\xc0K\xbe\xcf\x1f\xe4\xae\x1f_\x94\xa5\x92\x19a\xfb\xc7_\xe9\x82\xe4\xb8\xc0\xab\xb8\x9at(\xd8{\xa8}\x03\xf8\x82\xa5Ōve\x1f\xf9\a\x85t\xde;Į\xa7r\xb5ر놈\xee\xb3\xf7X\x94\x03kMĈͤ1\xc3'Ҁ\x1e\x01\xe9\xcbU\tT\xac\x04\x18\r\n\x17\x13R\xbb\x1a\xe7\x10RO\x1a#\xc4U\x19\x19\xa0]5t\x14\xd51\xd7\aR\xf5*,\x05\xdaӻ\xe7?⠁($\xce\xff\xd88\xa4\x15\x86$\xfc\xfeN\x81Ȁ\x1d?\xe9j>\\\xf7\xa9\xf9\x8fٷ\x8a\x1f\xaa;\x86z2\xb6\x96ykkGTbO\x93 \x10Y\x86\xa4\xbb_O\xbfYwy\xc9\xff\xa4\xcf\xd2\xf1\xbf\x99\xd1\xe1,u\xb7\xf0\x9f\xffu\x011\xcf\xf4\x9c\xf0\xa0\xce\xff\x0f\x00\x00\xff\xff\xe4Y]G\xe4O\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"), @@ -37,7 +37,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y_s۸\x11\u007fק\xd8\xf1=\xb87\x13RMz\xd3\xe9\xe8\xed\xcen:n\xef\x1cO\xe4\xcbK&\x0f\x10\xb1\x14Q\x93\x00\x8b]HV;\xfd\xee\x9d\x05HI\x94(Y\xce\xf4rzI\b,\x16\xbf\xfd\xed\x1f,\xe0I\x96e\x13՚O\xe8\xc98;\x03\xd5\x1a|f\xb4\xf2E\xf9\xd3_(7n\xbaz\xbb@Vo'O\xc6\xea\x19\xdc\x04b\xd7|Dr\xc1\x17x\x8b\xa5\xb1\x86\x8d\xb3\x93\x06Yi\xc5j6\x01P\xd6:V2L\xf2\tP8\xcb\xde\xd55\xfal\x896\u007f\n\v\\\x04Sk\xf4q\x87~\xff\xd5\x1f\xf3w\xf9\x0f\x13\x80\xc2c\\\xfeh\x1a$VM;\x03\x1b\xeaz\x02`U\x833h\x9d^\xb9:4\xe8\x91\xd8y\xa4|\x855z\x97\x1b7\xa1\x16\v\xd9u\xe9]hg\xb0\x9bH\x8b;Dɚ\a\xa7?E=\x1f\x93\x9e8U\x1b\xe2\u007f\x8cN\xffl\x88\xa3H[\a\xaf\xea\x11\x1cq\x96\x8c]\x86Z\xf9\xe3\xf9\t@\xeb\x91Я\xf0W\xfbd\xddھ7Xk\x9aA\xa9j\x92i*\\\x8b3\xb8\x17\xa4\xad*PO\x00V\xaa6:\U00091c3b\x16\xed\x8f\x0fw\x9f\xfe4/*lT\x1a\x14ͮEϦ7Q~{\xdeݎ\x01h\xa4\u009b6j\x84kQ\x95d@\x8b?\x91\x80+\x84\xce+\xa8\x81\xe26\xe0J\xe0\xca\x10x\x8c6\xd8\xe4\xe1=\xb5 \"ʂ[\xfc\x13\v\xcea.vz\x02\xaa\\\xa8\xb5\x04\xc1\n=\x83\xc7\xc2-\xad\xf9\xf7V3\x01\xbb\xb8e\xad\x18;\x86\xfb\x9f\xb1\x8cުZH\b\xf8\x06\x94\xd5Ш\rx\x94= \xd8=mQ\x84r\xf8\xc5y\x04cK7\x83\x8a\xb9\xa5\xd9t\xba4\xdc\xc7s\xe1\x9a&XÛi\x8cJ\xb3\b\xec\xdc\xf1,!\xf2+\xa5\xca?(\xae^\xdc\xf5\xfa\xaeL\xdbĊ\xc0\x0e\x14\xb4\x06\v\x1c\x94V0\x96\x18\x95N\x83#*\x01$q\xbf\xfb2\xc6\x19\xc0{\xe7\x01\x9fU\xd3\xd6\xf8\x06Lby[\xd0\xfa\xf80\x94\x88\xd8ꃵ\xe1ʌ\x1b\xae$\x8e:\x83\xd7\xd1PVO\b\xae34 \xd4\xe6\tgp%\x19\xbc\a\xf1?\x92:\xff\xbd\x1a\xd5\xf9\x87\x94\"W\"r\x95\x80mϬ\xfd\x8c\xdb\x01\xe4J1\xb07\xcb%z\x1cg3\x16b)p߃\xf3b\xbbu{\n\xa2Z\xf1Y\xaa3\xa8\x8f\x00\u007f~\xf7\xe5\x04\xda!O`\xac\xc6gx\a\xc6&VZ\xa7\xbf\xcf\xe11F\xc4Ʋz\x96}\x8a\xca\x11Zp\xb6ތ\xa3uP\xa9\x15\x02\xb9\x06a\x8du\x9d\xa5^A\xc3Zm\xc4\xfe\xde]\x12a\nZ\xe5y\xd8\r\x8cj}\xfcp\xfba\x96PI\b-c\x1d\x93S\xa64r\xe6\xcba\x9fN.\x89\xc9HGH\xc1\xc1\x0e\x8aJّ\xb2\x06\xb1i\x88\xec\x96AΒ\xfc\xfa\xb5\xd9zxl\xf7\xbf\x91\xe3\xfb\xb00\xfcN\x87\xe0Ef\xc5\xd6\xf9E\xb3\xee\xf7\xe2\xf9\xacY\xd2\xc4{\x8b\x8c\xd12\xed\n\x12\xa3\nl\x99\xa6n\x85~ep=];\xffd\xec2\x93@\xccR$\xd04\xb6\xe1\xd3\xef\xe2?_eE\xec\x8c/3%\x8a~\v{d\x1f\x9a\xbeڜ\xbe\xaf\xbb\xf4T\xba\x9ew\x8d\xc7\xe1JI\x89ue\x8a\xaao\xd2w\xd5s4G\x1a\xa5S\xc9Uv\U000db1ed\x10\x19\xbc\xe0\xd9d\xdd]0SV\xcb\xff\xc9\x10\xcb\xf8\xab\x99\v\xe6\x82$\xfd\xf5\xee\xf6\xdb\x04s0\xaf\xce\xc8ц4\xc5D\xeb\xee\xb4\xd0W\x1a\xf4g\xbb\xa9\x8f\x03Ѿ\v\x1c\xe9\xe3\xb62\x177rdUK\x95\xe3\xbb۳\b\xe6[\xb1~\xf7\x1d\xe5]\xfb\xd6k\x92\x10=ӷ\x9dD\x92ԜE\x91\xfa\xee\xb1.\xb8Ð:\x868\"\x1d\xe8W!\x91됴9\xfbH\xb2\xf1\x0e~ \xd1:=\xf8\x1e\xfaw0\xb5#}0\x9c\x8cx\xf12Ê\x03]~\x9d\x89\xe2=g)?\xb9S\x12\xcf\uebfa\xd0\x14N\x9a\xb9\xe1\xe3\xcd9\xcf\xdd\x1c\xcb\xc7\x17\x02\xaf\x13.6\r\xc6\xdbBD\x00kE\xfd\x16\xc7~\x83=mia\xac\x84\xa2\ful\xb6\xa4\x0f,\x95\xa9Q\xc3\xf6\xe9\b\x1e\xe5>\x17\xaf\xcc\xd7ǵ\xb2W\x13\bu\xbc\xe7\x8d\x00>\\U:\xdf(\x9e\x81\\\x933Qp0oC]\xabE\x8d3`\x1f\x0e'O\xa6A\x83Djy>\x0f~I2\xe9\x86\xd5-\x00\xb5p\x81\xb7W\xac.!:\xf3\xaf\xa9\xf3\xf8\xe5\x17\xbcJ\xd1y\x10\x0f\"1\x16Wۤ<\x17X\x10o/\xa19\xdc\"\x83{\\\x1f\x8d\xdd\xd9\a\xef\x96\x1e\xe9\xd0\aY﨣\xf6;\x83\xf71\x02.6\xb8\xdb\xe0\xbc͝\x10T\xae\xee#ױ\xaa\xc1\x86f\x81^\f_l\x18\xa9g\xa0O\xf4\xe3\x1bj\xecyw\xbc\xed\xd6\xf7\xd5*)\xea:\xf8B\xd9\xf8$#\xd1\xc9\x0e\xb4\xa1\xb6V\xc7-|oC<\xf6$8%Cvq\xd1g\x97\xa4t\x9c{͝:¹uv\xb4#\xebS\xc1X\xfe\xf3\x0f'\xcfGc\x19\x97\x83R\xd8\xcd\n\x85?\x89\xfe\xff\xb7\ue4c7/\xb1\xf2|Y\xe9\x9a\x0fD_\xaaZQ\xf1X\xcd\xda/?\xc7\xe5f\xb8ɷ\xa84#\xd4\x1c\f\xed\x1e\xec\xdf\uefa2\x8b\xb2\xee\x81>N@2K\xefm\xde=Fu#\xbb\x03K\x15\xd2k\xa1\xbe?|\xa1\xbf\xba\x1a<\xb8\xc7\xcf\xc2Ym\xd2_\x17\xe0\xf3\x97\ttOT\x9fz\x1c2\xf8\xbf\x00\x00\x00\xff\xff6\x10(\x86\xdc\x18\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4W\xcdn\xe36\x10\xbe\xfb)\x06\xdb\xc3^*\xb9i{(tk\xd3.\x104\t\x16\xce6\x97\xa2\a\x8a\x1a\xd9\xd3P$\xcb\x19:u\x9f\xbe %ٲ-{\x83\x05V7\x0e\x873\xdf|\xf3CjQ\x14\xc5Byz\xc6\xc0\xe4l\x05\xca\x13\xfe+hӊ˗\x9f\xb8$\xb7\xdc\xde\xd4(\xeaf\xf1B\xb6\xa9\xe06\xb2\xb8n\x85\xecb\xd0\xf8+\xb6dI\xc8\xd9E\x87\xa2\x1a%\xaaZ\x00(k\x9d\xa8$\xe6\xb4\x04\xd0\xceJp\xc6`(\xd6h˗Xc\x1d\xc94\x18\xb2\x87\xd1\xff\xf6\xbb\xf2\xfb\xf2\xc7\x05\x80\x0e\x98\x8f\u007f\xa2\x0eYT\xe7+\xb0ј\x05\x80U\x1dV\x10\x90\x85t@\xef\x98\xc4\x05B.\xb7h0\xb8\x92܂=\xea\xe4v\x1d\\\xf4\x15\x1c6\xfa\xd3\x03\xa4>\x9cU6\xb4\x1a\r\xed\xf2\x96!\x96\xdfg\xb7\xef\x89%\xabx\x13\x832s@\xf26\x93]G\xa3\u0099Br\xe0\x032\x86-\xfea_\xac{\xb5\x1f\bM\xc3\x15\xb4\xca0.\x00X;\x8f\x15<&\xa8^il\x16\x00[e\xa8Ɍ\xf4\xe0\x9dG\xfb\xf3ǻ\xe7\x1f\x9e\xf4\x06;\xd5\v\x93e\xe71\b\x8d1\xa6o\x92߽\f\xa0Aց|\xb6\b\uf4e9^\a\x9a\x94Qd\x90\r\u0090\x17l\x80\xb3\x1bp-Ȇ\x18\x02\xe6\x18l\x9f\xe3\x89YH*ʂ\xab\xffF-%<\xa58\x03\x03o\\4M*\x83-\x06\x81\x80ڭ-\xfd\xb7\xb7\xcc .\xbb4Jp\xa0x\xfc\xc8\n\x06\xabL\"!ⷠl\x03\x9d\xdaA\xc0\xe4\x03\xa2\x9dX\xcb*\\\u0083\v\bd[W\xc1F\xc4s\xb5\\\xaeIƊ֮\xeb\xa2%\xd9-s]R\x1d\xc5\x05^6\xb8E\xb3dZ\x17*\xe8\r\tj\x89\x01\x97\xcaS\x91\x81\xdb\\\xd0e\xd7|\x13\x86\xf2\xe7\xf7\x13\xa4\xb2Kic\td\xd7{q\xae\xb2\x8b\xbc\xa7\"\x03bPñ\x1e\xff\x81\xde$J\xac\xac~{\xfa\x04\xa3Ӝ\x82c\xce3ۇc| >\x11E\xb6\xc5\xd0'\xae\r\xae\xcb\x16\xd16ޑ\x95\xbcІ\xd0\x1e\x93α\xeeHR\xa6\xff\x89Ȓ\xf2S\xc2m\xeek\xa8\x11\xa2o\x94`S\u009d\x85[ա\xb9U\x8c_\x9d\xf6\xc40\x17\x89\xd2\xcf\x13?\x1dGNJ=[{\xf18-f3t\xda\xffO\x1euJXb-\x1d\xa4\x96t\xee\x01h]\x00u\xa6_N\f\xcf5g\xfaj\xa5_\xa2\u007f\x12\x17\xd4\x1a\uf75e\xb4\xf9\x05T\xbf̝\x18a\xa5\x11\xd77*\xce+\x9eX\x06\x90\x8d\x92I\x87\x8a\"\xbbo\xf3\x998.R\x9eiW\xa9]\xad\xb2\x1a?\xe4ڱzw5\x96\x87\x99\x03)\x94\x8d{\x05\xd7\nک\xc9\x11e\x8dgA\x84h\xdf\f\xb2\x9f\xc9wM*\xad\x960\\\x05\xb8:Q\x1eyn\xa31\x83\xa5B\xbb\xce+\xa1\xda\xe0\xd8ȭ\vg\x10\xa9\xb7\xb1\xeb\xbb\xfa\xcb\xf8\xdd:\x13;\xdc\xdf\rW\x91?\x1f\xebN\v\xa4\x17\f R\b\x10\x8e\xaf\xc0\xe97\xd4\x04\x83w\xcd\x00`(ZNq\xbe\x11{J.\x05<\x9a\x86\xc5|\xf1\x1fi\xccUԑ\xc2i6\x8f6O\xf8\xfa\xec0\x10%\x91\xdf>\x0e\xb2\xfaH\xac\x8e!\xa0\x95\xc1H\xbe\t\xbfh \x18\xc52i\x8b\xf4\x06\xba\x9a\xe7\xfbs\xfd\x11R2\x05\x92\x04\xd3.zU<\xd7/\xad\v\x9d\x92\n\xd2h/ҡ\x93\xfd\xf4\x02S\xb5\xc1\n$\xc4\xd3\xcd\xcb\x13\x01\x99\xd5\xfaz\x04\x0f\xbdN\u007f\x15\x0e\a@\xd5.\xca\x05b\xf3\xa5x\x85ګ\x88\xfcF\xf1u<\x1f\x93\xc6\\Z\xf1\xad\xce\xd1\xc6\xee\xd4E\x01\x8f\xf8z&[\xa1jN{\xae\x80G's\x1b\x17b\x9a\xa9\xe5\x13\xd1\xe1\x89}sX\xe5\xba+\x86'u\xde\x00\xc8/\xd3f\x92b\xee{s\x90\x1c\x1aDi\x8d^\xb0y<}R\xbf{w\xf4B\xceK\xedlC\xfd\xff\x00\xfc\xf9ע\xb7\x8a\xcd\xf3\x88#\t\xff\x0f\x00\x00\xff\xfft\x8f\x1aC\x8e\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xd4YMo\xf3\xb8\xf1\xbf\xfbS\f\xf2?<\xbb@\xac\xfc\x9f\xb6\x87·4O\x16\bv7\r\x92\xa7\xe9\xa1聖\xc66\x1b\x8a\xd4\xf2ʼn[\xf4\xbb\x173$\xf5bɲ\x17\xedb[]\fS\xc3\xe1̏\xf3\xae\xc5r\xb9\\\x88F\xbe\xa2u\xd2\xe8\x15\x88F\xe2\x87GM\xff\\\xf1\xf6{WHs\xb3\xff\xbcF/>/ޤ\xaeVp\x17\x9c7\xf53:\x13l\x89_p#\xb5\xf4\xd2\xe8E\x8d^T\u008b\xd5\x02@hm\xbc\xa0eG\u007f\x01J\xa3\xbd5J\xa1]nQ\x17oa\x8d\xeb U\x85\x96O\xc8\xe7\xef\xff\xbf\xf8M\xf1\xbb\x05@i\x91\xb7\u007f\x955:/\xeaf\x05:(\xb5\x00Т\xc6\x15Xt\xdeXt\xc5\x1e\x15ZSH\xb3p\r\x96t\xd8֚Ь\xa0{\x11\xf7$A\xa2\x12\xcfq;\xaf(\xe9\xfc\xf7\xfd\xd5\x1f\xa4\xf3\xfc\xa6Q\xc1\n\xd5\x1dƋN\xeamP¶\xcb\v\x80ƢC\xbb\xc7?\xe97m\xde\xf5w\x12U\xe5V\xb0\x11\xca\xd1kW\x9a\x06W\xf0HR4\xa2\xc4j\x01\xb0\x17JV\xacb\x94\xcb4\xa8o\x9f\x1e^\u007f\xfbR\xee\xb0\x16q\x11\xa0BWZ\xd90]\x96\x0f\xa4\x03\x01\xaf\xac\x1f\t\xc1\x17\x01~')\x0eX7\xfep\rB\xa9\xbe\x03\n\xdb\x01\xf8\xeb\x1aԅ\x8e\xf5pL}ƱN#46\x8e>F\x9d\xa5%\xba\x14Y\xff\v\x00Sb\x8d\xea\x05\x15\x96\xde\xd8Y\xb0~\xe8SF\xa0(/\xee?\x17\xc37\xde\xc0F*\x8f\x16ޥߍ\x14xߡN8QE\"u%\xf7\xb2\nB\r\xac\xac\x87R\a&\x18\vZ\xaa\xeb\x11O\xc28\xef\x1e`\n\u007fd\xe1\x85\xfaY>x*\xc5\xd2S\v_\xee\xee?\xa88r]\xf73\x03\xdb\xf1\x86\x88\\N_\f?\xb8\x8c\x1dUH\xd2b\xcdu\xd7\x04g\x80\xafle\x1d\x15\xeb{\xfb\xf8el@pڈFB\xde\xce\b\x92|\xa2\xbd^\xca.9\x11Or\x86T9_\x83\x807<\xc4\"\x9b\xea\xf8\x86Bifa\x91\xcbs\xbe\xe87<0Q\xaa\xb8'\xb9\xce]J|\xde\xf0p\xeaՑ\xbat^\xaa~\xa2\u07b4\xc0Rq=\x96U\xe5\xee\nO)I\x8f7\xd3\xc2¼\xa7\xe6'#r\xa1\xd8-\x80\xbd.\x90!\xfe\xe4\"\x9cd_;\x19\x1b\xbc\x19\xa9\x1d\xb2\xed\xe5\xfe\xe6\x95:Ֆy\xb4\xa8\a}\r\x8f\xc6\xd3\xcf\xfd\x87\xa4\x9a]\xe8q\x06\xed\x9e/\x06ݣ\xf1L\xfboA\x12\x85\xba\x10\x90H\xcc\x06\xaacl#\xbd\xfa\xed\x8f\xe3\xe8A\xb7\x9a\xf5\x9bQB:jA\x8c͚s\xdb\x1a\x8f\x88\xcc\xeb\xe0\xb8c\xd1F/9\"e\xee3L\xdbK\x93.Ci\xec\x00\xaf\x13\a\xcd\xf0\\#\xa4\xe3\xbfR#\x16\xf7\xc4VZ\x89\x12+\xa8\x02C\xc0\xad\xa0\xf0\xb8\x95%\xd4h\xb7sr6\x14\xa7N_\xddL$\x89\xcf\x05w{:\v\xe5'\x85\x9dj\xfa\xa0%\xd9\xfa\x897\xb3\xd7;٫]&\x15\x87oNp\x93ڋ\xaa\x921\xc3<\x9d\x89Og\xf0\x19\xe7\x8cxhJ\xb4\xa2!\xcb\xfe\a\x85S6\x94\u007fB#\xa4u\x05\xdc\xf2\xbcJM\xdfl\x9f>U\x1e}\xd6\xc4U: \xcc\xf7BQ\xa8\xa7\xc0\xa1\x01\x15\a\xfeI\x96f3\xcah\xd7\xf0\xbe3.F\xf1\x8dD\xc5S\x88\xab7<\\]\x0f<\x0f\xe4t(\xbdz\xd0W1I\x8c\xfc\xa0m\xf8\x8cV\a\xb8\xe2wW\xc5(\tN\xb2\x9dM\x8c3\x16q\xf2U[\xe9\xfe(\x9aF\xea\xed\xf1=_f\v3v0\xb0\x81ǣ\xd3\x06\x86\xd0/K\a%\xfc\xf8\xb88\xe6\x9b(\xf6m\x1e6jo\n\xb8Շ\x11WG\x1d\xe3D\xa9;\xec I\xa4w\xa9\x14E\xa5ijb\xa6}Fi\xb0\xe0D\x1d\xf9O\x0f\r&@O\x1c\x9f^\xe7+\xf9\xe7\x96l\xa2\x0f\xec)K\x95b\xab\xc0\xd3\xeb\xd8r\xb8\xf8tZ4ng<|\xb3\x97\"M\xb5L\xa8\x1ak\xf6\xd4\x0f~\xfb\x1f\xea\xe8\\\xb9\xc3*(<;\xb4y\xe9\x11\x9e\x1f\xdbd\xb6c[\xe8ph;\xb9\x8cV\x15=p8\x1eJ-L\xe2K\x97<\xd5D\xb7\f\xa39\x18\xc73Y\n'.\x94%:\xb7\t*w<<\xf6\xa7\xf6:\x92K\xd7J{\xe1\x18i*C,{ï\xb3\x13:/|pggtL\x05\xa5h|\xb0\xa9H-\x83\xb5\xacT|g6\xa31\xdd\x05S:\xb4\xd6\xd83\xd3\x1e&\x89\xee^\x9a\xa0\xb9V#\xbb\xe5\xbdP\xa3sb\x9b\xc7<\xefh\x11\xb6\xa8)\xc2N\x8c8R\x1d\x80\x1fX\x86\xf4\x91`اR$\x15\xa5\xa7\xf6+\x8a\xc6a\xb3u\xe2S9\x93\b\xc4\xf6ĝI\xedq\x8b\xc3L\xbc\x11R\x05\x8b\xcf(\xdc\xf0\xdb\xc1H\xfd\xef\xfa\x94\xa9\xb4\x8b\x9a\xc7\xceC\x04\x87U\x1a;{i\xf1\xe4l\x92\xfaP!G\x9d\xdf\xc9\xf8\xdb섛w\xc3'\xa2\xc8\xfe\xd77\x87\xd6\x03\x9f'eA\x1d\xeac\xc6Kx\xc4\xf7\xd1\x1a)\x8f\xd5k\xfb\xe9hD𠟬\xd9R\xe6\x1d\xbd\xba3u\xa3pl\x05Kx\x12\xd6K\xa1\xd4!\xb2?q\xea\xa58u\x1f\xb6\xee\xcf\x1b\xf3\xeb\x11\xf1р\x85̺\xe3\x97M\xf0\x1b9\x1e\xad\xa5/]k\x85\xdf\xfe:\x83\x92wa\xb5\xd4\xdbyu\xff\x9c\x88&\xbc7\xed\xff\xe5\xfc7\v8\xf4\xe0\x13\xb3\xbc\x9f\xeb\xc1\x13\xb1\xf4h\xa9\xfb\xc4\xfb\xb9\xfb\xc7h-\xd3']~Aݧ\xddc\xd5\xc3>\x89\x92V\xba\x00-\xca\x12\x1b\x9f&\x98\xfd\x8f\xbbWW\xfc'\u007f\xbd忥ѱ\xfar+\xf8\xcb_\x17\x90\x10x\xcdr\xd0\xe2\xbf\x02\x00\x00\xff\xffF\x9c\x18\xb7\x0e\x1f\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\x1c\xcbn#\xb9\xf1\xee\xaf(8\x87I\x00K\xce$9\x04\xba\xcdz\xbc\x88\x91\xc9\xec`\xc7\xf1%ȁ\xea.Y\x8c\xba\xc9\x0eɖ\xad\x04\xf9\xf7\xa0\x8a\xec\xa7\xfa\xc1\xf6z\x82\xcd\xc2<\xd9\x14Y,֛\xc5b_\xacV\xab\vQ\xc8\a4Vj\xb5\x01QH|v\xa8\xe8?\xbb>\xfcѮ\xa5\xbe>\xbeߢ\x13\xef/\x0eR\xa5\x1b\xb8)\xad\xd3\xf9\x8fhui\x12\xfc\x88;\xa9\xa4\x93Z]\xe4\xe8D*\x9c\xd8\\\x00\b\xa5\xb4\x13\xd4m\xe9_\x80D+gt\x96\xa1Y=\xa2Z\x1f\xca-nK\x99\xa5hx\x85j\xfd\xe3o\u05ff[\xff\xe1\x02 1\xc8\xd3\xefe\x8e։\xbc\u0600*\xb3\xec\x02@\x89\x1c7`\x93=\xa6e\x86v}\xc4\f\x8d^K}a\vLh\xb5G\xa3\xcbb\x03\xcd\x0f~R\xc0\xc4\xef\xe2k\x98\xcf]\x99\xb4\xeeϝ\xeeO\xd2:\xfe\xa9\xc8J#\xb2\xd6z\xdck\xa5z,3a\x9a\xfe\v\x80\u00a0EsĿ\xaa\x83\xd2O\xea{\x89Yj7\xb0\x13\x99\xa5\x9fm\xa2\v\xdc\xc0g¤\x10\t\xa6\x17\x00G\x91ɔ\xf7\xe9q\xd3\x05\xaa\x0f_\xee\x1e~Ox\xe4\xc2w\x02\xa4h\x13#\v\x1eW\xa3\b҂\x80\a\xde$\x98\xc0\x0ep{\xe1\xc0 \xe3\xa2\x1c\x8d(\f\xae*,S\xd0&\xc0\x04(\xd0H\x9d\xca\x04\xbe\x13ɡ,\xfcT\xbb\xd7e\x96\xc2\x16\xc1\x94j\x1d\xc6\x16F\x17h\x9c\xacHH\xad%5u_\x0f\xd3w\xb4\x15?\x06R\x92\x13\xb4\xe0\xf6\b\x81ۘ2\xf5r\x01z\an/m\x837\x93\xa4\x05\x16h\x88P\xa0\xb7\xff\xc0ĭ\xe1+\xd1\xd9\xd8\n\xdbD\xab#\x1a\xdaw\xa2\x1f\x95\xfcW\rقӼd&\x1c\x06\x8eVM*\x87F\x89\x8c\x98P\xe2\x15\b\x95B.N`\x90րR\xb5\xa0\xf1\x10\xbb\x86\xbfh\x83 \xd5No`\xef\\a7\xd7\u05cf\xd2Uz\x92\xe8\xae\x84I\xf6\xd2a\xe2J\x83ע\x90+F\\\xb1\x9a\xac\xf3\xf4W\x15\x17\xed\xbb\x16\xa6\xeeDbc\x9d\x91\xea\xb1\xeef!\x1e\xa5;ɲ\x17\x0f?\xcd\xe3ߐ\x97\xba\x88*?\xde~\xbdo\x8b\x8e\xb4]\x9a3\xb5[\xd2\xd4\x10\x9e\b%\xd5\x0e\x8dg\xdc\xce\xe8\x9c!\xa2J\v-\x95\xe3\u007f\x92L\xa2\xea\x12ݖ\xdb\\:\xe2\xf4?K\xb4\x8e\xf8\xb3\x86\x1b\xb6\x16$se\x91\n\x87\xe9\x1a\xee\x14܈\x1c\xb3\x1ba\U0005b4dd(lWD\xd2y·\x8d\\w\xa0\xa7V\xdd]\x19\xa3A\x0eU:\xfc\xb5\xc0\xa4\xa3\x1a4K\xeed\xc2\n\x00;m\x1a\x15\xb7m\x8b5\xae\x97\xbcv\x18\xda흴#7F+\xc0g\xe2t\xa3\xaf$'O{T\xa4E\xa6T\x84a\x0f\"\x04\xe3\xb1\xee\xf5\x0fҎ\u007f\xc0\xbc e\x9cD\xed>\f\"Ԉ*i\xedd\xbc\xa9\xc0\xdad\xe9`\xa9@\x0fcW\x18}\x94i\xb0\a=\xeaMQ\x90\x1a>'Y\x99bZ[\xec\x811=\xc4oϦ\xb0\xe3\x13R\x11\x8d\xc9\xcd\xd0\x06T\xf3+\xd9\xdc\x01\xa0\x00\xc2 \x90RH\xe5!\x82\xe4\r\xc2v\x90\xdcԤ\xc3|\x10\xc3\tn\xf8F\x8eUl3܀3\xe59\r\xab\xf9\xc2\x18q\x1a\xa5R\x15\x10\xc4\x13\xa9\x9e\x11LU&\x13$\xf2\xd4\x06\x89\xe9\xf4\v \xd1^\xeb\xc3\x1c\x03\xe27\x13\x02\xaa\xfa\x84\xe5\xb34W \xe0\x80'\x1f\x05\t\x05\xc4(AK\x8d\x1e$Ή\xc4\xe9\"6\x11\a<1\xa0\x90\x01\x8a\x98\x1f/\x1a\xbe\x1d\xf0\x147\xb0GJ\xc2,\x9c\x8f=M\xa9\x83\t\u0089\x84%d\x04\xce籆\x80\xd31\x9b\x84%\xe6\xa6j\x15'^\xb4ݚ\x8d\x9d\xe4\xe6\x01O\xef\xacg\x18i\xc7^\x16\xd1\x1b&\x03\f\x16Y\x8f\xaa\xfcރ\xc8dZ/\xe5\xf5\xe1NME\xdd\xdd\xf6Y\xbb;u\x05\xb7\xcf\xd2\x12z*\x85\x8f\x1a\xedg\xed\xb8\xe7\x9b\x11֣\xff\"\xb2\xfa\xa9\xaczʛy\xa2G;m\x18%\xf4\xbe\xdd\xf9\x13f\xcd*i\xe1N\xd1Y)Ѕ\x93\xbf\f3^,\x19\xa5\xbc\xb4\x9c\x1fTZ\xad\xd8Ѯ\a֊\x86\x19أM\x87;m\xf4Z\xcbFC\xa5\x03\x9dG\xed\x9e|\x8f\x87\xe0\x93ڙH0\x85\xb4d\xa2\x8ah\x88\xd6\x19\xe1\xf0Q&\x90\xa3yD(\xc8\x17\xc4r#\xda>\xfb\xb6X\xe6bC\x83\xaa\x05C\x9fƠ\xb4\"\xbd\x8e\x1aW\xb1?b\xf0`\x96vzp\xcc\xde\xd8As\x1c\x13Am\x91\xa6\x9c\xc6\x14ٗE^b\x11w\xce\xe3\a\x8f\x9e\xf7\xaf\xb9(H\xc3\xffM.\x92\x85\xfd?P\bi\xa2\xb4\xfc\x03_|eؙ\x1d\xb2n\xed\x85h\ri\x818~\x14Y\xff\x0e`d\x8b\x9a,\x10f>\x14л\xb3\xc8\xe7\n\x9e\xf6\xdaz\x8f\xbc\x93\x98\r'\xbe\xbaMZ\xb8<\xe0\xe9\xf2\xea\xcc.]ީK\x1f\"\xf4\xb5>\x02l\x1dqh\x95\x9d\xe0\x92g_\xfe\xb4p*Z:#\a\xf2-i| N'\xd9*\x9a\xa0\xa9\xf5\x95\x1c\x85\xd0\xd3\xd8G\xcaf\xa1\xad[\x80\xd0\x17m\x9dψv\x02\xdee\xf96\xf0r\x15\xf2l v\x0e\rX\xa7Mu\x01FF\xb2\x976&.ڹ\x03\a1\xb6\xce\xdey\xb0t\x98\xbbl\xf4\xdb\xdb\xdfK\u007f3F\u007f\xcfAL8\x80aȅ\xd1\tZ;'6Q\x16~&\xb1Y'5\x85?,\xf1\x8dӬ\xb0\xce%[\xab\xb6$\x14&r.>N\xdc>\xb7\xf2\xb2d>\xe8\xffy\x91]\x8e\x1d\xb0\xd6\xe7\xb9PQ\x0e\xec\f\xd1\x1b?\xb7R\xb1\x00\xca\x1fQ\xccc\xc9\xe6bI\xe4\x1a\x84\xef\xe7\x13\f\xe4R\xdd\xf1\"\xf0\xfe\x9b\x84\x0f\xb5\xd1ŗ\x1d\x1fn\xaa\xd9\r\v\xea\x8e\xe1\xabñVh\xbe\xaf0\xd8\xe1\xe4yV\u007fQج\xb4k\xa7>\br\xa1\xd3w\x16v\xd2X\xd7 \x1b\rSZ(g-HӖ\x9f\xe5ԭ1/<\xca\xfd\xe0綒q{\xfdT_s3!\x17D\xdd{qD\x90;\x90\x0eP%\xbaT\x9c4\"c\xc0\x8bxv\xc4\v2\xc4\xfa\xbd\xa6\xa1*\xf3XB\xacX\x12\xa5\x9a\xc9/\xb5'|/d\xf6\xad\xd8\xe8d\x8e\xba\x9cq\xccM\xeb\xde\xd2\xfb\xb9\x9d\xfa\x85\\<˼\xccA\xe4Ĉh\x92S\xb4!s\xec\xca\x00<\t\xe9\xd8#\x11dvONG\x83Lt^d\xe8\x10\xb6\xb8ӆ\xf5\xdd\xca\x14k\xd7\x1f\xe4B\x8f\xdf;\xf6\x9b\x80\x9d\x90Yi\xa2\xad\xeeBn,;!\x05\xc3\xf3\xba\a\x9fX\x14VL\xbe\xa8tttH;\xe7\t\n\xb3$\xa0\xfdb\xf0\xb5\xc3\xc7\xc2H\x92E=\x17A\xce@\xe4\xf8\xb2\x1bA\x06\x11\x15\xea4\x16B\xce\xc0d,\xdeBȷ\x102\x0e\xee[\b\xf9\x16Bη\xb7\x10\xf2-\x84\x8c\x99\xf0\x16B\xbe\x85\x90\xcbQ\xf8߇\x90\xf3\x98\xad8\xf79\xfas\x046Q%\x04\xd3\xc8N\xae\x12\xaaan\xb2\xd2:4\xf15\xb7w\xc3\xf3Z\xf6\xf3i\x8fn\x8f\x06\x12?d\xc5\xcfU\x86e\xa3)\xb7h\\V]\x84K\xcaV)\x8a\xafb\x9f\x8d\x8e#\x8bk\xb7Zg(Ε-\xae\x94k\xae\x80\xab[\x83\\\x17OUE\xc8\xc3V#,\x1d\xb8\xe5\xdfA\xb4\xab\x81\xbauX\x1c\x99W\xd8\xfe\x8cꓣJ\xacf\n\xab\xa6K\xb8u\xb5\xc6\b\x15;\t\xf8.\xf9L\xa7\x8c\xf8gH\xbd\xd9ڧ\xf1\x8a\xa7p#\x87N\x1c߯\xbb\xbf8\x1d\xea\x9f\xe0I\xba\xfd\xe0\xa6\xf8e\x06\x1d\x17\xd5c\xbb0\xba\x92\xc5\xf0\xf2\xa9OU\xd0\x06\x94̆k\x1a\x88\xe0\xd5\xfc\x0e\xb9\xe1\x87\xc2\x1fJ_\xa4\xbfsǤ\xd8\x1a\xa9\x17WFu\xeb\x9eF\r\xfc\xf2\v\xbc%e\xe5\xf1\xb5Os\xa5JK*\x9e\xda\xd5L\x13 c\xeb\x9c\xe2N\xbc\xb35M/\xa8d\x8a\xae\xab|\x95\xebʸZ\xa5oQ\xa1\xb4\xa0.\xa9[o4\x03wY5R$\x99b*\x8f\x16\xd7\x1b\x85ڞ\x99\xfdDT\x19\x8dV\x0f̀\x1e\xa8c\x9a\xaf\x19\x9a#\u007f\a\x95W\xa9\x14zA}\xd0\xeb\xd6\x11\xbfV\xd4=U\xed\x13Q\xe3\x13\x11\x97\xcfa\x1aQų\xacv'\x82\x86/\xacө\xabpF\xd7^Z\x9dӭ\xbd\x19\x05\x1bS\x933Rq3\ns\xb2\x12'\xb6\xcef\x14\xfa\xac\xfb\x9e\x91\x9cɟ\xad\x12\x85\xddk\xf7\xa0\xb32\x8f\x88\x99\xbfv\xc7\x0f\x1c\xbd(b\x13\a\x84$\xd3eZ\xc3\x1f\xde\x1e\xbf\x87?\xc1\x97\av.\xfc\xd0/i\x9e@\x06\xf7Q\x85r\xfd\x17\x92\xc3ov\xe1\xa7\x1fŬ\xd3F<\xe2'\x9d\xb4>j0E\x93\xee\xf8Λ\xf5\xc0\xfc*\xd9\x12\xaa\x92\x86\xc3ְ\xa3>\xb8&\xc7\xea\xa3\xe0\xd6y\x950\x1d\x96\x8bI\xcdu.\x9b\xdd\xd4\xfd\xfd'\xbf\x11's\\\u007f,\xfd1xU\bc\x91h[m\xd0Oڎ\x19\x88\xbd~\x82L\x87\xdd\u007f\xd7\xc7\xdf gs\xf9\xbc\xbdx\x17G\x16\xc1J +r͋\xf0\xc3\xf0\xbcV\xe4\xddb\x1a\x1f\xfd\xc6dw\f\x92\xb0V'R\xf0\x9bP\xe9\xc23\x8eo\xf1\xa0w\xdc!\x8c(\xfd\x90\x1f[\r=+_\xd5o\xdc/f\x80Z'\\i\xe7?\x12\xc0\xc3 \x11\x85+MH\x92&\xa5᧽\x04\x02\xfd\v\xd8\xe5\x9f\tȄu^\xb0&_\xe3\u007f\xaa\x875Q\xbau>\xdbZi\x1e<\t\xcb\xef\xf0}\xaeU\xda\xee\aV\xdam\xe4%\xfeN\x9b\\\xb8\r\xa4\xc2\xe1\x8a`\xf7~\x9f\xb4L\xa3\xcc\xe6\xa7ϓ\xbb\xfbB#\xea\x1b\x9d@V\x9eV=\x98\x1e\xd9\xc9P\xca~\x05\x9f\xf1\xe9\xac\xefV\x11\xe2\xfd\\\x9a\xcf\xcac\xfaP\u007f\x03&vS\xcdWc\xf8\x12\xe4L\x01\xba:\xdb\x1b\xdc\xcb\xd4Љ\xbf\x81\xe7/<,\xfcZ\x9e\x1bZ>~%\xb4\x93\xdf\xf4~\x1b\xd1\xc2\t\r\x1c־\x01%\xe9u5\xdf\tz\xdf\xfc\xc7K\xaf\xc2w\x81\x8e\xfe\xee\x90?ē\xb6d%x\xa6\xd0\xd3h\x9eH\x12,\\\xc8\x04\xb6?\x10ty\xc9\xffT\xdf\xff\xe1\u007f\x13\xad|\fh7\xf0\xb7\xbf_@\xf0\"\x0f\x15\x1e\xd4\xf9\xdf\x00\x00\x00\xff\xff\xcb\xe6҉SI\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\x1c\xcbn\xe4\xb8\xf1\xee\xaf(8\x87I\x00w;\x93\xe4\x10\xf4m\xd6\xe3E\x8cLf\akǗ \a\xb6T\xedf,\x91\nI\xb5\xdd\t\xf2\xefA\x15\xa9g\xebAy\xed`\xb30O\xdd\x14Y,֛\xa5\xa2\xceV\xabՙ(\xe4=\x1a+\xb5ڀ($>;T\xf4Ϯ\x1f\xffh\xd7R_\x1e>nщ\x8fg\x8fR\xa5\x1b\xb8*\xad\xd3\xf9\x8fhui\x12\xfc\x8c;\xa9\xa4\x93Z\x9d\xe5\xe8D*\x9c\u061c\x01\b\xa5\xb4\x13\xd4m\xe9/@\xa2\x953:\xcbЬ\x1eP\xad\x1f\xcb-nK\x99\xa5hx\x85j\xfd\xc3o\u05ff[\xff\xe1\f 1\xc8\xd3\xefd\x8e։\xbc\u0600*\xb3\xec\f@\x89\x1c7`\x93=\xa6e\x86v}\xc0\f\x8d^K}f\vLh\xb5\a\xa3\xcbb\x03\xcd\x03?)`\xe2wq\x1b\xe6sW&\xad\xfbs\xa7\xfb\x8b\xb4\x8e\x1f\x15YiD\xd6Z\x8f{\xadT\x0fe&L\xd3\u007f\x06P\x18\xb4h\x0e\xf8W\xf5\xa8\xf4\x93\xfa^b\x96\xda\r\xecDf\xe9\xb1Mt\x81\x1b\xf8J\x98\x14\"\xc1\xf4\f\xe0 2\x99\xf2>=n\xba@\xf5\xe9\xdb\xcd\xfd\xef\t\x8f\\\xf8N\x80\x14mbd\xc1\xe3j\x14AZ\x10pϛ\x04\x13\xd8\x01n/\x1c\x18d\\\x94\xa3\x11\x85\xc1U\x85e\n\xda\x04\x98\x00\x05\x1a\xa9S\x99\xc0w\"y,\v?\xd5\xeeu\x99\xa5\xb0E0\xa5Z\x87\xb1\x85\xd1\x05\x1a'+\x12RkIM\xdd\xd7\xc3\xf4\x03mŏ\x81\x94\xe4\x04-\xb8=B\xe06\xa6L\xbd\\\x80ށ\xdbK\xdb\xe0\xcd$i\x81\x05\x1a\"\x14\xe8\xed?0qk\xb8%:\x1b[a\x9bhu@C\xfbN\xf4\x83\x92\xff\xaa![p\x9a\x97̄\xc3\xc0ѪI\xe5\xd0(\x91\x11\x13J\xbc\x00\xa1R\xc8\xc5\x11\f\xd2\x1aP\xaa\x164\x1eb\xd7\xf0\x17m\x10\xa4\xda\xe9\r\xec\x9d+\xec\xe6\xf2\xf2A\xbaJO\x12\x9d祒\xeex\xc9\xd2.\xb7\xa5\xd3\xc6^\xa6x\xc0\xec\xd2ʇ\x950\xc9^:L\\i\xf0R\x14rň+V\x93u\x9e\xfe\xaa\xe2\xa2\xfd\xd0\xc2\xd4\x1dIl\xac3R=\xd4\xdd,ģt'Y\xf6\xe2\xe1\xa7y\xfc\x1b\xf2R\x17Q\xe5\xc7\xebۻ\xb6\xe8Hۥ9S\xbb%M\r\xe1\x89PR\xed\xd0x\xc6\xed\x8c\xce\x19\"\xaa\xb4\xd0R9\xfe\x93d\x12U\x97\xe8\xb6\xdc\xe6\xd2\x11\xa7\xffY\xa2uğ5\\\xb1\xb5 \x99+\x8bT8L\xd7p\xa3\xe0J\xe4\x98]\t\x8boNv\xa2\xb0]\x11I\xe7\t\xdf6r݁\x9eZuwe\x8c\x069T\xe9\xf0m\x81IG5h\x96\xdcɄ\x15\x00v\xda4*n\xdb\x16k\\/y\xed0\xb4\xdb;iG\xae\x8cV\x80\xcf\xc4\xe9F_IN\x9e\xf6\xa8H\x8bL\xa9\b\xc3\x1eD\b\xc6c\xdd\xeb\x1f\xa4\x1d?\xc0\xbc e\x9cD\xed.\f\"Ԉ*i\xedd\xbc\xa9\xc0\xdad\xe9`\xa9@\x0fcW\x18}\x90i\xb0\a=\xeaMQУ\xb4\x13e\xe6~D\xebdr\xfa\xb8\x87\xf3\xe7\xf6芏h\x89~n\x8f\x86Ԍ\x1f\xd4\xf6\xb5\xb4\xec\x02N\x1b\x99,\xf1\x88\xb0\xf5{$˗eP\xe8\x14\x0e:+s\xb4\xb0=V\xc8\xf5\xe9\xde\xd0~\xabu\x86B\x9d<\xc7\xe7$+SLk?4\xb0\xf3\xde֮O\xa6\xb0;\x17R\x91\xe4\x90\xf3$$U\xf3\x94<\xc9\xe0քA U\x97\xcaC\x04\xc9l\v[\x1dڌt\x98\x0fb8!c\xbeQ\xb8 \xb6\x19n\xc0\x99\xf2T2\xaa\xf9\xc2\x18q\x1c\xa5R\x15\xe6\xc4\x13\xa9\x9e\x11\fp&\x13$\xf2\xd4f\x96\xe9\xf4\v \xd1^\xeb\xc7y\xb2\xfc\x89F5.\x04\x12\x8e\x1ea\x8b{q\x90\xda\xd8~ԁϘ\x94nD3\x84\x83T\xeevh\bR\xb1\x17\x16me\x10\xc6\xc93\xa5\xe2\xd4\xcc\x14\x8bO\xf6Ӱ\x97\x18\xc54\x18\xdb\x02\x9b\xce\x11\x98\xc0\b\x93}-\v\x90*\x95\a\x99\x96\"\x03\xa9\xac\x13*\xf1\xfb\x125nC\xfb\x82i֟`\xeeMf\x85?\xf1\xa5\xe3}\xb4B\xd0\x06rr\xb5\xa7C\xed\xe8\x1a0\xba\xfd\xad\xb0\x14pz\xf15\x14;\x87\xc5Rvl\x8d\xbd\xb8\x98\x00^s\xc7\ah\x99\xd8b\x06\x163L\x9c6cd\x99g\xbaoq\xb6p\x84\x9e\x03V\xb1\xb1\xfa\xb4\xe5f\x83\x93@\xd9\xe0?\xede\xb2\xf7\xb1\x14\xc9\x14C\x82T\xa3e[ \x8a\";\x8eo\x16\xe6%!,4m\x0e\x9a6k\x18\xfa0\x87MDӢ\xeci\xd3f,k\x97ε\x88\xbc\x93\xb9r\x1a/\x12蛓ɯ-\xd0D`I稛\x1d`^\xb8\xe3\x05HW\xf5\xceä0\xa8\xc1\xe1\x17\xc1\xa8\x97\xe8\xc3M\u007f\xee+\xeb\xc3+p\xa9F\xe1\xff\x9aI\xecln\x83\xafY\xc0\xa0/\xedy\x17 w5\x83\xd2\v\xd8\xc9\xcc!\xc7=\xd3(\xb6\\\xdf,\xa7^\x8b,q^\x93Z.\\\xb2\xbf\xae\x8f\x8b\xb3\xe3{\x14\xeaO\xf7\xb1ru\x92\xe8:\xf9Y\xc8\xc0\xd9\x04i0\xf79\x8a;ց\xa6\x87#\xb5O_?c:M(\x88\x95ȓ\xed|\xea\xa1\xdc^>\x1c\x03\xe27\x13\x02\xaa\xfa\x84\xe5sO\x17 \xe0\x11\x8f>\n\x12\n\x88Q\x82\x96\x1a=H\x9c\x12\x89\x93`l\"\x1e\xf1ȀB^+b~\xbch\xf8\xf6\x88Ǹ\x81=R\x12f\xe1\xd4\xefiJ\x1dL\x10N\x8f,!#p\x96\x925\x04\x9c\x8e\xd9$,17U\xab8\xf1\xa2\xed\xd6l\xec\xa4l\x1f\xf1\xf8\xc1z\x86\x91v\xece\x11\xbda2\xc0`\x91\xf5\xa8\xcaZދL\xa6\xf5R^\x1fn\xd4T\xd4\xddm_\xb5\xbbQ\x17p\xfd,-\xa1\xa7R\xf8\xac\xd1~Վ{ތ\xb0\x1e\xfd\x17\x91\xd5Oe\xd5S\xde\xcc\x13=\xda\xc9\xd0(\xa1\xf7\xedƟ0kVI\v7\x8a\xceJ\x81.\x9c\xd2f\x98\xf1b\xc9(\xe5\xa5嬧\xd2jŎv=\xb0V4\xcc\xc0\x1em:\xdci\xa3\xd7Z6\x1a*\x1d\xe8\x8e\xc0&\xaa\x84`\x1a\xd9\xc9UB5\xccUVZ\x87&\xbe\xe6\xf6fx\xde@\xf5u⇬\xf8\x12ΰl4\xe5\x16\x8d˪\x8bpI\xd9*E\xf1\xb5\xf9\xb3\xd1qdq\xedX\x9dv\\)\xd7\\\x01W\xb7\x06\xb9.\x9e\xaa\x8a\x90\x87\xadFX:p\xcb\xdf\xeehW\x03u\xeb\xb082\xaf\xb0\xfd\x19\xd5'G\x95X\xcd\x14VM\x97p\xebj\x8d\x11*v\x12\xf0]\xf2\x99N\x19\xf1ϐz\xb3\xb5O\xe3\x15O\xe1\x8d\x1c:q\xf8\xb8\xee>q:\xd4?\xc1\x93t\xfb\xc1M\xf1}\x13:.\xaa\x87vat%\x8b\xe1>W\x9f\xaa\xa0\r(\x99\r\xd74\x10\xc1\xab\xf9\x1dr\xc3\x0f\x85?\x94\xbeH\u007f\xe7\x8eI\xb15R/\xae\x8c\xea\xd6=\x8d\x1a\xf8\xe5/\U00016515\xc7\xd7>͕*-\xa9xjW3M\x80\x8c\xads\x8a;\xf1\xce\xd64\xbd\xa0\x92)\xba\xae\xf2U^W\xc6\xd5*\xbdE\x85҂\xba\xa4n\xbd\xd1\f\xdce\xd5H\x91d\x8a\xa9\x87iD\x15ϲڝ\b\x1a\xbe\xb0N\xa7\xae\xc2\x19]{iuN\xb7\xf6f\x14lLM\xceH\xc5\xcd(\xcc\xc9J\x9c\xd8:\x9bQ\xe8\xb3\xee{Fr&\x1f[%\n\xbb\xd7\xee\xde_5\x9d\x8d\xfcn\xbb\xe3\a\x8e^\xd5u\xd6$\xd3eZ\xc3\x1f\xde\x1e\xdf\xf2?·{v.|\xd1/i\xae@\x06\xf7Q\x85r\xfd\x1b\x92\xc37\x91\xe1\xa7\x1fŬ\xd3F<\xe0\x17\x9d\xb4>\xd50E\x93\xee\xf8\xceM\xfc\xc0\xfc*\xd9\x12\xaa\x92\x86\xc3ְ\xa3>\xb8&\xc7\x1an\b7\xe7U\xc2tX.&5\u05f9lvSww_\xfcF\x9c\xccq\xfd\xb9\xf4\xc7\xe0U!\x8cE\xa2m\xb5A?i;f \xf6\xfa\t2\x1dv\xff]\x1f\u007f\x83\x9c\xcd\xe5\xf3\xf6\xe2]\xf8\xdbѕ@V\xe4\x9a\x17\xe1\xfb\xe1y\xadȻ\xc54>\xfa\x8d\xc9\xee\x18$a\xadN\xa4\xe0;\xa1҅k\x1coq\xa1w\xdc!\x8c(\xfd\x90\x1f[\r]\x96_\xd57\xf7\xcff\x80Z'\\i\xe7?}\xc0\xc3 \x11\x85+MH\x92&\xa5\u1afd\x04\x02\xfd\r\xd8\xe5\x1f?Ȅu^\xb0&\xbf1\xf0\xa5\x1e\xd6D\xe9\xd6\xf9lk\xa5y\xf0$,\u007f]\xc0\xe7Z\xa5\xed~6\xa6\xddF\xbe/\xb0\xd3&\x17n\x03\xa9p\xb8\"ؽ瓖i\x94\xd9|\xf5yrw\xdfhD\xfdF'\x90\x95\xa7U\x17\xa6Gv2\x94\xb2_\xc1W|:\xe9\xbbV\x84x?\x97\xe6\xb3\xf2\x98\xde\xd7_\xb6\x89\xddT\xf3-\x1c~\tr\xa2\x00]\x9d\xed\r\xeeej\xe8\xc4\xdf\xc0\xf3/<,\xfcZ\x9e\x1aZ>~%\xb4\x93\xdf\xf4\x9e\x8dh\xe1\x84\x06\x0ek߀\x92\xf4\xba\x9a\xaf\x1f}l\xfe\xf1ҫ\U000358c3\u007fwȟ\x17J[\xb2\x12\xfb\x00\xac)J\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x93\xe34\x10\xbd\xfbWt-\x87\xbd`\x87\x01\x0e\x94o\xd4\x00US\xc0\xd4\xd4d\x99\v\xc5A\x91\xdaI3\xb2dԭ\f\xe1\xd7S\x92\xecI\xe28;\xbb\x87\xf5\xcd\xed\xfexz\xfd\xba\xad\xaa\xae\xebJ\r\xf4\x84\x81ɻ\x16\xd4@\xf8\xaf\xa0Ko\xdc<\xff\xc0\r\xf9\xd5\xfef\x83\xa2n\xaagr\xa6\x85\xdb\xc8\xe2\xfbGd\x1f\x83Ɵ\xb0#GB\xdeU=\x8a2JT[\x01(缨d\xe6\xf4\n\xa0\xbd\x93\xe0\xad\xc5Po\xd15\xcfq\x83\x9bH\xd6`\xc8\x15\xa6\xfa\xfbo\x9ao\x9b\xef+\x00\x1d0\x87\u007f\xa0\x1eYT?\xb4\u08b5\x15\x80S=\xb6\xc0\x18R\x90(\x89\x1c\xf0\x9f\x88,\xdc\xec\xd1b\xf0\r\xf9\x8a\aԩ\xf06\xf88\xb4p\xfcP\xe2GP\xe5@\xeb\x9cj\x9dS=\x96T\xf9\xab%\x96_\xafy\xfcF\xa3\xd7`cPv\x19Pv`r\xdbhUXt\xa9\x00\x86\x80\xf9\xc3\x1f\xee\xd9\xf9\x17\xf7\v\xa15\xdcB\xa7,c\x05\xc0\xda\x0f\xd8\xc2}B=(\x8d\xa6\x02\xd8+K&\xd3S\xce\xe1\at?>\xdc=}\xb7\xd6;\xecU1\x02\x18d\x1dh\xc8~Kg\x00bP0\"\x01\xf1\xa0\xb4Ff\xd01\x04t\x02\x05)\x90\xeb|\xe8s\xb911\x80\xda\xf8( ;\x84\xa7L\xedx\xb6ft\x18\x82\x1f0\bMD琣\xcc^m3\x8c\xef\xd3!\x8a\x0f\x98$,\xe4\\c\x94\a\x1a\xe0|@\xf0\x1dȎ\x18\x02f\xf6\x9c\x9c\xa3˜t\xa0\x1c\xf8\xcdߨ\xa5\x19O\xcf\xc0;\x1f\xadIj\xdcc\x10\b\xa8\xfd\xd6\xd1\u007f\xaf\x999ѐJZ%\x93\x0e\xa6\x87\x9c`p\xca&\xfa#~\r\xca\x19\xe8\xd5\x01\x02\xa6\x1a\x10\xddI\xb6\xec\xc2\r\xfc\xee\x03f\x02[؉\fܮV[\x92i\xb0\xb4\xef\xfb\xe8H\x0e\xab<\x1e\xb4\x89\xe2\x03\xaf\f\xeeѮ\x98\xb6\xb5\nzG\x82Zb\xc0\x95\x1a\xa8\xce\xc0]\x9e\xab\xa67_\x85q\n\xf9\xfd\tR9$\xc1\xb0\x04r\xdbWs\x96\xfaUޓ̋\x1aJX\xc1\u007f\xa47\x99\x12+\x8f?\xaf?\xc0T4\xb7\xe0\x9c\xf3\xcc\xf61\x8c\x8f\xc4'\xa2\xc8u\x18J\xe3\xba\xe0\xfb\x9c\x11\x9d\x19<\xb9\xa2%m\t\xdd9\xe9\x1c7=\tO*M\xfdi\xe06\xaf\x17\xd8 \xc4\xc1(A\xd3\xc0\x9d\x83[գ\xbdU\x8c_\x9c\xf6\xc40\u05c9ҷ\x89?݊玅\xadW\xf3\xb4\xb2\x16;\xb40\xbd\xeb\x01u\xeaY\".\xc5RG:\x8f\x01t>\x80Z\ni\xdeĐ\xbd?\vŸ#\n\x8e\xd9\xe6H3\xf8\x16\x8e\xa5U\x91\xed;\xc5xn\x9a\xa1yH\x1e\xf3ʖ:\xd4\am\xb1$(\x9b\x02\xdf\x02\x91\x1et\xb1\x9f\u05eb\xe1\x1e_.l\x0f\xc1\xa7=\x99W\xf1\xe9\xb3\xd8\u007f(\xff\x88-9\xfe\xf8i\x8aO\xfe뜮ܓU;\xa6\x81\x10\x9dK\x13\xe9]2ϒ\xc2\xf9F\x9e}%\xc1\xfe\x02\xc7\"\x92;\xd7\xf9\xfc\xd7V\xa9\xa4\x922'86u\xacQ\x10]\xa4\xbb\xd6\xd3\xf2\xccW\xd1'\x10X\x9e\xfc\xe7\xff\xfc\xc0\xb4:(\xe0B\xcd:cY0\xa7J\x17\xe6ʼn\x19\x91Ek\xd5\xc6b\v\x12\xe2<\xb2ĩ\x10\xd4\xe1\\\x15\x93\x8c\x8ew\x9c\x8f\n\xe4\xc2=i\xffe\x87\xee\x9a\xc2\xe1E\xf1RoJ\x1a\xd8\x1c\xae\x05\u07be^\xd6\xe6CRd\xd9Bں\xb5\xd0\x05K\x9f@\xc4B\x97\x8aT\x17n\a\x17$\xacO=\xa7\xd9?\x13\xfctY\x98#\xbfR|\xa1\xa93\xd3\xf1nzs|\xcb®ǻh\xfe0\x9e\u009c\x9c\x9c\xc5\a\xb5\x9d\xb88\xee\xd6t\xcd\x1a\x04\xcd\xfd\xfc&\xfa\xee\xddٕ2\xbfj\xef\f\x95\x8b4\xfc\xf9WU\xb2\xa2y\x9ap$\xe3\xff\x01\x00\x00\xff\xff\x8e\xadi\xa0\xc7\v\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V\xc1\x8e\xdc6\f\xbd\xfb+\x88\xf4\x90K\xed\xe9\xb6=\x14\xbe\x05\xdb\x16\b\x9a\x06\x8bl2\x97\xa2\a\x8dDϰ+K\xaaH9\xdd~}!Y\xde\x19\xcf\xce$-\x8a\xf8&\x9a\"\x1f\x1f\x1f\t5m\xdb6*\xd0\x16#\x93w=\xa8@\xf8\x97\xa0\xcb'\xee\x1e~\xe0\x8e\xfcf\xba١\xa8\x9b恜\xe9\xe16\xb1\xf8\xf1\x1d\xb2OQ\xe3\x8f8\x90#!\xef\x9a\x11E\x19%\xaao\x00\x94s^T6s>\x02h\xef$zk1\xb6{t\xddC\xda\xe1.\x915\x18K\x86%\xff\xf4M\xf7m\xf7}\x03\xa0#\x96\xeb\xefiD\x165\x86\x1e\\\xb2\xb6\x01pj\xc4\x1e&oӈ\xecT\xe0\x83\x17\xeb\xf5\x9c\xac\x9b\xd0b\xf4\x1d\xf9\x86\x03\xea\x9c{\x1f}\n=\x1c\u007f\xcc!*\xae\xb9\xa6m\x89v_\xa3\xbd\xa9ъ\x83%\x96_>\xe1\xf4\x86X\x8ac\xb0)*{\x15Y\xf1ar\xfbdU\xbc\xe6\xd5\x00\x84\x88\x8cq\xc2\x0f\xee\xc1\xf9\x8f\xeegBk\xb8\x87AY\xc6\x06\x80\xb5\x0f\xd8\xc3\xdb\\AP\x1aM\x030)K\xa6ܟk\xf2\x01ݫ\xbb\xd7\xdb\xef\xee\xf5\x01G5\x1b\x01\f\xb2\x8e\x14\x8aߕb\x80\x18\x14,h\xe0\xe3\x01#¶0\a,>\"W\xe05$\xc0R\x01w\xd5\x14\xa2\x0f\x18\x85\x16\x82\xf3w\xa2\xb0'\xdb\x19\x9e\x97\x19\xf0\xec\x03&k\n\x19\xe4\x80P\x95\x81\x06\xb8\x14\x03~\x009\x10C\xc4\u0094\x93c\xab\x96\xcf\x0f\xa0\x1c\xf8\xdd\x1f\xa8\xa5\x83\xfb\xccfd\xe0\x83O\xd6d!N\x18\x05\"j\xbfw\xf4\xf7Sd\x06\xf1%\xa5U\x82\xb5\xa7\xcbGN0:e3\xd5\t\xbf\x06\xe5\f\x8c\xea\x11\"\xe6\x1c\x90\xdcI\xb4\xe2\xc2\x1d\xfc\xea#\x02\xb9\xc1\xf7p\x10\t\xdco6{\x92e\xa6\xb4\x1f\xc7\xe4H\x1e7e2h\x97\xc4G\xde\x18\x9c\xd0n\x98\xf6\xad\x8a\xfa@\x82ZRč\n\xd4\x16\xe0nV\xf9h\xbe\x8au\x00\xf9\xe5\tRy\xcc\xe2`\x89\xe4\xf6O\xe6\"\xf1\xab\xbcgm\xcfm\x9f\xaf\xcd\xf8\x8f\xf4fSf\xe5\xddO\xf7\xefaIZZ\xb0漰}\xbc\xc6G\xe23Q\xe4\x06\x8cs\xe3\x86\xe8\xc7\x12\x11\x9d\t\x9e\x9c\x94\x83\xb6\x84nM:\xa7\xddH\x92;\xfdgB\x96ܟ\x0en\xcbf\x81\x1dB\nF\t\x9a\x0e^;\xb8U#\xda[\xc5\xf8\xc5i\xcf\fs\x9b)\xfd<\xf1\xa7\vq\xed8\xb3u\x1c\xa2\xba\xaa.v\xe8\xf2\xa4\xde\aԫA\xc91h\xa0:\xb9\x83\x8f\xa0Vl\xd6)\xbe\x1c\xad;q\xbd4\xc00o\xf0\x81\xf6k\x1b\x802\xa6l\u007fe\xef\xaeܻJυZoK\x8e,\xc7\\@\x88~\"\x83\xb1]j\xab\x18R\xacE\x96\xdd\xd85\x97r\x9d1\\\v+\xe1\xce\xe1\xad\x10\xdcU\xa7\x8c!Ӻ\\\x9a\xf7\x0e\xd6\xf5W\x96\xa1\xda\xe3\xe5\xdc\xcf\xea\xcc\n\xa6\x88\xab)l\x9fB\u007fV\x1d\xa2$\xf1\u007f\xd5G\xb9T=wU#:ňNjD\xf0\xc3\n\xbe\xfa\xff\x1a\t\a\xc5\xf8I~/Ǿ\xcb\xf7\x16\xca-\r\xa8\x1f-\xce\xd1\xca6\u007f&\xa8\u007f\x8d4\u007f\xe8\xd2x\x0e\xaa\x85W\x93\"\xabv\x16\x9f\xfd\xf9\xe0ԕ\u007fW\xfa{\xa1mg\xa6\xe3\x03\xe7\xe6x*\xe4\xb5˃\xe6f~!\xe4\xa5iz\x90\x98\xe6\xe4Ui\xd5rԂ\xd2\x1a\x83\xa0y{\xfe\x96y\xf1b\xf5\x1c)G\xed\xdd<\xa6\xdc\xc3o\xbf7sT4\xdb\x05G6\xfe\x13\x00\x00\xff\xff\x9f\xfe\xa5\x85\f\n\x00\x00"), } diff --git a/pkg/generated/crds/manifests/velero.io_backups.yaml b/pkg/generated/crds/manifests/velero.io_backups.yaml index 7f68b2d1c..af1492426 100644 --- a/pkg/generated/crds/manifests/velero.io_backups.yaml +++ b/pkg/generated/crds/manifests/velero.io_backups.yaml @@ -36,6 +36,10 @@ spec: spec: description: BackupSpec defines the specification for a Velero backup. properties: + defaultRestic: + description: DefaultRestic specifies whether restic should be used to + take backup of all pod volumes by default. + type: boolean excludedNamespaces: description: ExcludedNamespaces contains a list of namespaces that are not included in the backup. diff --git a/pkg/generated/crds/manifests/velero.io_schedules.yaml b/pkg/generated/crds/manifests/velero.io_schedules.yaml index dc05b1976..67218dd8b 100644 --- a/pkg/generated/crds/manifests/velero.io_schedules.yaml +++ b/pkg/generated/crds/manifests/velero.io_schedules.yaml @@ -44,6 +44,10 @@ spec: description: Template is the definition of the Backup to be run on the provided schedule properties: + defaultRestic: + description: DefaultRestic specifies whether restic should be used + to take backup of all pod volumes by default. + type: boolean excludedNamespaces: description: ExcludedNamespaces contains a list of namespaces that are not included in the backup. From f34aab251e68e3a8943e990ff85b4fcae8fe9c90 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Thu, 4 Jun 2020 15:16:05 -0700 Subject: [PATCH 02/10] add default restic flag to backup create cli Signed-off-by: Ashish Amarnath --- pkg/builder/backup_builder.go | 6 ++++++ pkg/cmd/cli/backup/create.go | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/pkg/builder/backup_builder.go b/pkg/builder/backup_builder.go index 6d0b1dc68..c9cdedf61 100644 --- a/pkg/builder/backup_builder.go +++ b/pkg/builder/backup_builder.go @@ -128,6 +128,12 @@ func (b *BackupBuilder) SnapshotVolumes(val bool) *BackupBuilder { return b } +// DefaultRestic sets the Backup's "DefaultRestic" flag. +func (b *BackupBuilder) DefaultRestic(val bool) *BackupBuilder { + b.object.Spec.DefaultRestic = &val + return b +} + // Phase sets the Backup's phase. func (b *BackupBuilder) Phase(phase velerov1api.BackupPhase) *BackupBuilder { b.object.Status.Phase = phase diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index 2ba0986c9..ad6345a80 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -81,6 +81,7 @@ type CreateOptions struct { Name string TTL time.Duration SnapshotVolumes flag.OptionalBool + DefaultRestic flag.OptionalBool IncludeNamespaces flag.StringArray ExcludeNamespaces flag.StringArray IncludeResources flag.StringArray @@ -123,6 +124,9 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { f = flags.VarPF(&o.IncludeClusterResources, "include-cluster-resources", "", "include cluster-scoped resources in the backup") f.NoOptDefVal = "true" + + f = flags.VarPF(&o.DefaultRestic, "default-restic", "", "use restic by default to backup all pod volumes") + f.NoOptDefVal = "true" } // BindWait binds the wait flag separately so it is not called by other create @@ -295,6 +299,9 @@ func (o *CreateOptions) BuildBackup(namespace string) (*velerov1api.Backup, erro if o.IncludeClusterResources.Value != nil { backupBuilder.IncludeClusterResources(*o.IncludeClusterResources.Value) } + if o.DefaultRestic.Value != nil { + backupBuilder.DefaultRestic(*o.DefaultRestic.Value) + } } backup := backupBuilder.ObjectMeta(builder.WithLabelsMap(o.Labels.Data())).Result() From 8a2a852b8768848f21dc9d44cbb1810854cc179a Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Wed, 3 Jun 2020 17:15:59 -0700 Subject: [PATCH 03/10] use backup's defaultRestic flag to identify pod volumes using restic Signed-off-by: Ashish Amarnath --- pkg/backup/item_backupper.go | 2 +- pkg/cmd/server/server.go | 1 + pkg/controller/backup_controller.go | 7 ++ pkg/controller/backup_controller_test.go | 93 +++++++++++++++++++++ pkg/restic/common.go | 51 ++++++++++++ pkg/restic/common_test.go | 102 +++++++++++++++++++++++ 6 files changed, 255 insertions(+), 1 deletion(-) diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index e0889ff0c..11691ef65 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -141,7 +141,7 @@ func (ib *itemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstr // Get the list of volumes to back up using restic from the pod's annotations. Remove from this list // any volumes that use a PVC that we've already backed up (this would be in a read-write-many scenario, // where it's been backed up from another pod), since we don't need >1 backup per PVC. - for _, volume := range restic.GetVolumesToBackup(pod) { + for _, volume := range restic.GetPodVolumesUsingRestic(pod, boolptr.IsSetToTrue(ib.backupRequest.Spec.DefaultRestic)) { if found, pvcName := ib.resticSnapshotTracker.HasPVCForPodVolume(pod, volume); found { log.WithFields(map[string]interface{}{ "podVolume": volume, diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index f4ecc866f..6861f4ce1 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -651,6 +651,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string backupTracker, s.sharedInformerFactory.Velero().V1().BackupStorageLocations().Lister(), s.config.defaultBackupLocation, + s.config.defaultRestic, s.config.defaultBackupTTL, s.sharedInformerFactory.Velero().V1().VolumeSnapshotLocations().Lister(), defaultVolumeSnapshotLocations, diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 05f549df5..c9edf49f0 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -71,6 +71,7 @@ type backupController struct { backupTracker BackupTracker backupLocationLister velerov1listers.BackupStorageLocationLister defaultBackupLocation string + defaultRestic bool defaultBackupTTL time.Duration snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister defaultSnapshotLocations map[string]string @@ -92,6 +93,7 @@ func NewBackupController( backupTracker BackupTracker, backupLocationLister velerov1listers.BackupStorageLocationLister, defaultBackupLocation string, + defaultRestic bool, defaultBackupTTL time.Duration, volumeSnapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, defaultSnapshotLocations map[string]string, @@ -120,6 +122,7 @@ func NewBackupController( volumeSnapshotLister: volumeSnapshotLister, volumeSnapshotContentLister: volumeSnapshotContentLister, newBackupStore: persistence.NewObjectBackupStore, + defaultRestic: defaultRestic, } c.syncHandler = c.processBackup @@ -339,6 +342,10 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg request.Spec.StorageLocation = c.defaultBackupLocation } + if request.Spec.DefaultRestic == nil { + request.Spec.DefaultRestic = &c.defaultRestic + } + // add the storage location as a label for easy filtering later. if request.Labels == nil { request.Labels = make(map[string]string) diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 87d457a93..9f6561ab7 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -48,6 +48,7 @@ import ( pluginmocks "github.com/vmware-tanzu/velero/pkg/plugin/mocks" "github.com/vmware-tanzu/velero/pkg/plugin/velero" velerotest "github.com/vmware-tanzu/velero/pkg/test" + "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/util/logging" ) @@ -342,6 +343,7 @@ func TestProcessBackupCompletions(t *testing.T) { name string backup *velerov1api.Backup backupLocation *velerov1api.BackupStorageLocation + defaultRestic bool expectedResult *velerov1api.Backup backupExists bool existenceCheckError error @@ -351,6 +353,7 @@ func TestProcessBackupCompletions(t *testing.T) { name: "backup with no backup location gets the default", backup: defaultBackup().Result(), backupLocation: defaultBackupLocation, + defaultRestic: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -370,6 +373,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + DefaultRestic: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -385,6 +389,7 @@ func TestProcessBackupCompletions(t *testing.T) { name: "backup with a specific backup location keeps it", backup: defaultBackup().StorageLocation("alt-loc").Result(), backupLocation: builder.ForBackupStorageLocation("velero", "alt-loc").Bucket("store-1").Result(), + defaultRestic: false, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -404,6 +409,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: "alt-loc", + DefaultRestic: boolptr.False(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -422,6 +428,7 @@ func TestProcessBackupCompletions(t *testing.T) { Bucket("store-1"). AccessMode(velerov1api.BackupStorageLocationAccessModeReadWrite). Result(), + defaultRestic: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -441,6 +448,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: "read-write", + DefaultRestic: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -456,6 +464,7 @@ func TestProcessBackupCompletions(t *testing.T) { name: "backup with a TTL has expiration set", backup: defaultBackup().TTL(10 * time.Minute).Result(), backupLocation: defaultBackupLocation, + defaultRestic: false, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -476,6 +485,7 @@ func TestProcessBackupCompletions(t *testing.T) { Spec: velerov1api.BackupSpec{ TTL: metav1.Duration{Duration: 10 * time.Minute}, StorageLocation: defaultBackupLocation.Name, + DefaultRestic: boolptr.False(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -492,6 +502,7 @@ func TestProcessBackupCompletions(t *testing.T) { backupExists: false, backup: defaultBackup().Result(), backupLocation: defaultBackupLocation, + defaultRestic: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -511,6 +522,83 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + DefaultRestic: boolptr.True(), + }, + Status: velerov1api.BackupStatus{ + Phase: velerov1api.BackupPhaseCompleted, + Version: 1, + FormatVersion: "1.1.0", + StartTimestamp: ×tamp, + CompletionTimestamp: ×tamp, + Expiration: ×tamp, + }, + }, + }, + { + name: "backup specifying a false value for 'DefaultRestic' keeps it", + backupExists: false, + backup: defaultBackup().DefaultRestic(false).Result(), + backupLocation: defaultBackupLocation, + // value set in the controller is different from that specified in the backup + defaultRestic: true, + expectedResult: &velerov1api.Backup{ + TypeMeta: metav1.TypeMeta{ + Kind: "Backup", + APIVersion: "velero.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: "backup-1", + Annotations: map[string]string{ + "velero.io/source-cluster-k8s-major-version": "1", + "velero.io/source-cluster-k8s-minor-version": "16", + "velero.io/source-cluster-k8s-gitversion": "v1.16.4", + }, + Labels: map[string]string{ + "velero.io/storage-location": "loc-1", + }, + }, + Spec: velerov1api.BackupSpec{ + StorageLocation: defaultBackupLocation.Name, + DefaultRestic: boolptr.False(), + }, + Status: velerov1api.BackupStatus{ + Phase: velerov1api.BackupPhaseCompleted, + Version: 1, + FormatVersion: "1.1.0", + StartTimestamp: ×tamp, + CompletionTimestamp: ×tamp, + Expiration: ×tamp, + }, + }, + }, + { + name: "backup specifying a true value for 'DefaultRestic' keeps it", + backupExists: false, + backup: defaultBackup().DefaultRestic(true).Result(), + backupLocation: defaultBackupLocation, + // value set in the controller is different from that specified in the backup + defaultRestic: false, + expectedResult: &velerov1api.Backup{ + TypeMeta: metav1.TypeMeta{ + Kind: "Backup", + APIVersion: "velero.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: "backup-1", + Annotations: map[string]string{ + "velero.io/source-cluster-k8s-major-version": "1", + "velero.io/source-cluster-k8s-minor-version": "16", + "velero.io/source-cluster-k8s-gitversion": "v1.16.4", + }, + Labels: map[string]string{ + "velero.io/storage-location": "loc-1", + }, + }, + Spec: velerov1api.BackupSpec{ + StorageLocation: defaultBackupLocation.Name, + DefaultRestic: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -529,6 +617,7 @@ func TestProcessBackupCompletions(t *testing.T) { backupExists: true, backup: defaultBackup().Result(), backupLocation: defaultBackupLocation, + defaultRestic: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -548,6 +637,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + DefaultRestic: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseFailed, @@ -564,6 +654,7 @@ func TestProcessBackupCompletions(t *testing.T) { backup: defaultBackup().Result(), existenceCheckError: errors.New("Backup already exists in object storage"), backupLocation: defaultBackupLocation, + defaultRestic: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -583,6 +674,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, Spec: velerov1api.BackupSpec{ StorageLocation: defaultBackupLocation.Name, + DefaultRestic: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseFailed, @@ -633,6 +725,7 @@ func TestProcessBackupCompletions(t *testing.T) { backupLocationLister: sharedInformers.Velero().V1().BackupStorageLocations().Lister(), snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), defaultBackupLocation: defaultBackupLocation.Name, + defaultRestic: test.defaultRestic, backupTracker: NewBackupTracker(), metrics: metrics.NewServerMetrics(), clock: clock.NewFakeClock(now), diff --git a/pkg/restic/common.go b/pkg/restic/common.go index 41f5c9f89..a73e95910 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -23,6 +23,7 @@ import ( "time" "github.com/pkg/errors" + corev1api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" corev1listers "k8s.io/client-go/listers/core/v1" @@ -53,6 +54,10 @@ const ( // need to be backed up using restic. VolumesToBackupAnnotation = "backup.velero.io/backup-volumes" + // VolumesToExcludeAnnotation is the annotation on a pod whose mounted volumes + // should be excluded from restic backup. + VolumesToExcludeAnnotation = "backup.velero.io/backup-volumes-excludes" + // Deprecated. // // TODO(2.0): remove @@ -111,6 +116,7 @@ func GetVolumeBackupsForPod(podVolumeBackups []*velerov1api.PodVolumeBackup, pod // GetVolumesToBackup returns a list of volume names to backup for // the provided pod. +// Deprecated: Use GetPodVolumesUsingRestic instead. func GetVolumesToBackup(obj metav1.Object) []string { annotations := obj.GetAnnotations() if annotations == nil { @@ -125,6 +131,51 @@ func GetVolumesToBackup(obj metav1.Object) []string { return strings.Split(backupsValue, ",") } +func getVolumesToExclude(obj metav1.Object) []string { + annotations := obj.GetAnnotations() + if annotations == nil { + return nil + } + + return strings.Split(annotations[VolumesToExcludeAnnotation], ",") +} + +func contains(list []string, k string) bool { + for _, i := range list { + if i == k { + return true + } + } + return false +} + +// GetPodVolumesUsingRestic returns a list of volume names to backup for the provided pod. +func GetPodVolumesUsingRestic(pod *corev1api.Pod, defaultRestic bool) []string { + if !defaultRestic { + return GetVolumesToBackup(pod) + } + + volsToExclude := getVolumesToExclude(pod) + podVolumes := []string{} + for _, pv := range pod.Spec.Volumes { + // cannot backup hostpath volumes as they are not mounted into /var/lib/kubelet/pods + // and therefore not accessible to the restic daemon set. + if pv.HostPath != nil { + continue + } + // don't backup volumes that are included in the exclude list. + if contains(volsToExclude, pv.Name) { + continue + } + // don't include volumes that mount the default service account token. + if strings.HasPrefix(pv.Name, "default-token") { + continue + } + podVolumes = append(podVolumes, pv.Name) + } + return podVolumes +} + // SnapshotIdentifier uniquely identifies a restic snapshot // taken by Velero. type SnapshotIdentifier struct { diff --git a/pkg/restic/common_test.go b/pkg/restic/common_test.go index 2a73c6039..1e223a2e2 100644 --- a/pkg/restic/common_test.go +++ b/pkg/restic/common_test.go @@ -419,3 +419,105 @@ func TestTempCACertFile(t *testing.T) { os.Remove(fileName) } + +func TestGetPodVolumesUsingRestic(t *testing.T) { + testCases := []struct { + name string + pod *corev1api.Pod + expected []string + defaultRestic bool + }{ + { + name: "should get PVs from VolumesToBackupAnnotation when defaultRestic is false", + defaultRestic: false, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + VolumesToBackupAnnotation: "resticPV1,resticPV2,resticPV3", + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should get all pod volumes when defaultRestic is true and no PVs are excluded", + defaultRestic: true, + pod: &corev1api.Pod{ + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + // Restic Volumes + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should get all pod volumes except ones excluded when defaultRestic is true", + defaultRestic: true, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + }, + }, + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + // Restic Volumes + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + /// Excluded from restic through annotation + {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should exclude default service account token from restic backup", + defaultRestic: true, + pod: &corev1api.Pod{ + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + // Restic Volumes + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + /// Excluded from restic because colume mounting default service account token + {Name: "default-token-5xq45"}, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should exclude host path volumes from restic backups", + defaultRestic: true, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + }, + }, + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + // Restic Volumes + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + /// Excluded from restic through annotation + {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, + // Excluded from restic because hostpath + {Name: "hostPath1", VolumeSource: corev1api.VolumeSource{HostPath: &corev1api.HostPathVolumeSource{Path: "/hostpathVol"}}}, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actual := GetPodVolumesUsingRestic(tc.pod, tc.defaultRestic) + + sort.Strings(tc.expected) + sort.Strings(actual) + assert.Equal(t, tc.expected, actual) + }) + } +} From dd11b175ec5ce3e6e793601e6a10d573797f1da6 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Fri, 5 Jun 2020 07:50:11 -0700 Subject: [PATCH 04/10] changelog Signed-off-by: Ashish Amarnath --- changelogs/unreleased/2611-ashish-amarnath | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/unreleased/2611-ashish-amarnath diff --git a/changelogs/unreleased/2611-ashish-amarnath b/changelogs/unreleased/2611-ashish-amarnath new file mode 100644 index 000000000..6c9ad598f --- /dev/null +++ b/changelogs/unreleased/2611-ashish-amarnath @@ -0,0 +1 @@ +implement option to back up all volumes by default with restic From b71173228ae0e654ad6c906078b616d052d7b5e0 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Fri, 5 Jun 2020 15:34:02 -0700 Subject: [PATCH 05/10] add velero server flag to allow default restic use on all pod volumes Signed-off-by: Ashish Amarnath --- pkg/backup/backup.go | 8 ++++++++ pkg/cmd/server/server.go | 4 ++++ pkg/restic/common.go | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index 18a5657d5..d97232a85 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -71,6 +71,7 @@ type kubernetesBackupper struct { podCommandExecutor podexec.PodCommandExecutor resticBackupperFactory restic.BackupperFactory resticTimeout time.Duration + defaultRestic bool } type resolvedAction struct { @@ -103,6 +104,7 @@ func NewKubernetesBackupper( podCommandExecutor podexec.PodCommandExecutor, resticBackupperFactory restic.BackupperFactory, resticTimeout time.Duration, + defaultRestic bool, ) (Backupper, error) { return &kubernetesBackupper{ backupClient: backupClient, @@ -111,6 +113,7 @@ func NewKubernetesBackupper( podCommandExecutor: podCommandExecutor, resticBackupperFactory: resticBackupperFactory, resticTimeout: resticTimeout, + defaultRestic: defaultRestic, }, nil } @@ -240,6 +243,11 @@ func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Req log.Infof("Including resources: %s", backupRequest.ResourceIncludesExcludes.IncludesString()) log.Infof("Excluding resources: %s", backupRequest.ResourceIncludesExcludes.ExcludesString()) + if backupRequest.Backup.Spec.DefaultRestic == nil { + backupRequest.Backup.Spec.DefaultRestic = &kb.defaultRestic + } + log.Infof("Backing up all pod volumes using restic: %t", *backupRequest.Backup.Spec.DefaultRestic) + var err error backupRequest.ResourceHooks, err = getResourceHooks(backupRequest.Spec.Hooks.Resources, kb.discoveryHelper) if err != nil { diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 6861f4ce1..44a2728d4 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -126,6 +126,7 @@ type serverConfig struct { profilerAddress string formatFlag *logging.FormatFlag defaultResticMaintenanceFrequency time.Duration + defaultRestic bool } type controllerRunInfo struct { @@ -152,6 +153,7 @@ func NewCommand(f client.Factory) *cobra.Command { resourceTerminatingTimeout: defaultResourceTerminatingTimeout, formatFlag: logging.NewFormatFlag(), defaultResticMaintenanceFrequency: restic.DefaultMaintenanceFrequency, + defaultRestic: restic.DefaultRestic, } ) @@ -213,6 +215,7 @@ func NewCommand(f client.Factory) *cobra.Command { command.Flags().DurationVar(&config.resourceTerminatingTimeout, "terminating-resource-timeout", config.resourceTerminatingTimeout, "how long to wait on persistent volumes and namespaces to terminate during a restore before timing out") command.Flags().DurationVar(&config.defaultBackupTTL, "default-backup-ttl", config.defaultBackupTTL, "how long to wait by default before backups can be garbage collected") command.Flags().DurationVar(&config.defaultResticMaintenanceFrequency, "default-restic-prune-frequency", config.defaultResticMaintenanceFrequency, "how often 'restic prune' is run for restic repositories by default") + command.Flags().BoolVar(&config.defaultRestic, "default-restic", config.defaultRestic, "backup all volumes with restic by default") return command } @@ -637,6 +640,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string podexec.NewPodCommandExecutor(s.kubeClientConfig, s.kubeClient.CoreV1().RESTClient()), s.resticManager, s.config.podVolumeOperationTimeout, + s.config.defaultRestic, ) cmd.CheckError(err) diff --git a/pkg/restic/common.go b/pkg/restic/common.go index a73e95910..e84981e02 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -46,6 +46,10 @@ const ( // at which restic prune is run. DefaultMaintenanceFrequency = 7 * 24 * time.Hour + // DefaultRestic specifies whether restic should be used, by default, to + // take backup of all pod volumes. + DefaultRestic = false + // PVCNameAnnotation is the key for the annotation added to // pod volume backups when they're for a PVC. PVCNameAnnotation = "velero.io/pvc-name" From b0fd3d35c10c2e22807b94d1cdafde72c280bd55 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Mon, 15 Jun 2020 15:26:44 -0700 Subject: [PATCH 06/10] rename field Signed-off-by: Ashish Amarnath --- pkg/apis/velero/v1/backup.go | 4 +- pkg/apis/velero/v1/zz_generated.deepcopy.go | 4 +- pkg/backup/backup.go | 12 +- pkg/backup/item_backupper.go | 2 +- pkg/builder/backup_builder.go | 6 +- pkg/cmd/cli/backup/create.go | 8 +- pkg/cmd/server/server.go | 10 +- pkg/controller/backup_controller.go | 10 +- pkg/controller/backup_controller_test.go | 122 +++++++++--------- pkg/generated/crds/crds.go | 4 +- .../crds/manifests/velero.io_backups.yaml | 6 +- .../crds/manifests/velero.io_schedules.yaml | 6 +- pkg/restic/common.go | 8 +- pkg/restic/common_test.go | 30 ++--- 14 files changed, 116 insertions(+), 116 deletions(-) diff --git a/pkg/apis/velero/v1/backup.go b/pkg/apis/velero/v1/backup.go index d0875506c..8d50672c2 100644 --- a/pkg/apis/velero/v1/backup.go +++ b/pkg/apis/velero/v1/backup.go @@ -83,11 +83,11 @@ type BackupSpec struct { // +optional VolumeSnapshotLocations []string `json:"volumeSnapshotLocations,omitempty"` - // DefaultRestic specifies whether restic should be used to take + // DefaultVolumesToRestic specifies whether restic should be used to take a // backup of all pod volumes by default. // +optional // + nullable - DefaultRestic *bool `json:"defaultRestic,omitempty"` + DefaultVolumesToRestic *bool `json:"defaultVolumesToRestic,omitempty"` } // BackupHooks contains custom behaviors that should be executed at different phases of the backup. diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index bb22e0635..7e5e8ad39 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -246,8 +246,8 @@ func (in *BackupSpec) DeepCopyInto(out *BackupSpec) { *out = make([]string, len(*in)) copy(*out, *in) } - if in.DefaultRestic != nil { - in, out := &in.DefaultRestic, &out.DefaultRestic + if in.DefaultVolumesToRestic != nil { + in, out := &in.DefaultVolumesToRestic, &out.DefaultVolumesToRestic *out = new(bool) **out = **in } diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index d97232a85..766c8e732 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -71,7 +71,7 @@ type kubernetesBackupper struct { podCommandExecutor podexec.PodCommandExecutor resticBackupperFactory restic.BackupperFactory resticTimeout time.Duration - defaultRestic bool + defaultVolumesToRestic bool } type resolvedAction struct { @@ -104,7 +104,7 @@ func NewKubernetesBackupper( podCommandExecutor podexec.PodCommandExecutor, resticBackupperFactory restic.BackupperFactory, resticTimeout time.Duration, - defaultRestic bool, + defaultVolumesToRestic bool, ) (Backupper, error) { return &kubernetesBackupper{ backupClient: backupClient, @@ -113,7 +113,7 @@ func NewKubernetesBackupper( podCommandExecutor: podCommandExecutor, resticBackupperFactory: resticBackupperFactory, resticTimeout: resticTimeout, - defaultRestic: defaultRestic, + defaultVolumesToRestic: defaultVolumesToRestic, }, nil } @@ -243,10 +243,10 @@ func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Req log.Infof("Including resources: %s", backupRequest.ResourceIncludesExcludes.IncludesString()) log.Infof("Excluding resources: %s", backupRequest.ResourceIncludesExcludes.ExcludesString()) - if backupRequest.Backup.Spec.DefaultRestic == nil { - backupRequest.Backup.Spec.DefaultRestic = &kb.defaultRestic + if backupRequest.Backup.Spec.DefaultVolumesToRestic == nil { + backupRequest.Backup.Spec.DefaultVolumesToRestic = &kb.defaultVolumesToRestic } - log.Infof("Backing up all pod volumes using restic: %t", *backupRequest.Backup.Spec.DefaultRestic) + log.Infof("Backing up all pod volumes using restic: %t", *backupRequest.Backup.Spec.DefaultVolumesToRestic) var err error backupRequest.ResourceHooks, err = getResourceHooks(backupRequest.Spec.Hooks.Resources, kb.discoveryHelper) diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index 11691ef65..c85a6dc7a 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -141,7 +141,7 @@ func (ib *itemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstr // Get the list of volumes to back up using restic from the pod's annotations. Remove from this list // any volumes that use a PVC that we've already backed up (this would be in a read-write-many scenario, // where it's been backed up from another pod), since we don't need >1 backup per PVC. - for _, volume := range restic.GetPodVolumesUsingRestic(pod, boolptr.IsSetToTrue(ib.backupRequest.Spec.DefaultRestic)) { + for _, volume := range restic.GetPodVolumesUsingRestic(pod, boolptr.IsSetToTrue(ib.backupRequest.Spec.DefaultVolumesToRestic)) { if found, pvcName := ib.resticSnapshotTracker.HasPVCForPodVolume(pod, volume); found { log.WithFields(map[string]interface{}{ "podVolume": volume, diff --git a/pkg/builder/backup_builder.go b/pkg/builder/backup_builder.go index c9cdedf61..4bcd53fc6 100644 --- a/pkg/builder/backup_builder.go +++ b/pkg/builder/backup_builder.go @@ -128,9 +128,9 @@ func (b *BackupBuilder) SnapshotVolumes(val bool) *BackupBuilder { return b } -// DefaultRestic sets the Backup's "DefaultRestic" flag. -func (b *BackupBuilder) DefaultRestic(val bool) *BackupBuilder { - b.object.Spec.DefaultRestic = &val +// DefaultVolumesToRestic sets the Backup's "DefaultVolumesToRestic" flag. +func (b *BackupBuilder) DefaultVolumesToRestic(val bool) *BackupBuilder { + b.object.Spec.DefaultVolumesToRestic = &val return b } diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index ad6345a80..5061f436d 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -81,7 +81,7 @@ type CreateOptions struct { Name string TTL time.Duration SnapshotVolumes flag.OptionalBool - DefaultRestic flag.OptionalBool + DefaultVolumesToRestic flag.OptionalBool IncludeNamespaces flag.StringArray ExcludeNamespaces flag.StringArray IncludeResources flag.StringArray @@ -125,7 +125,7 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { f = flags.VarPF(&o.IncludeClusterResources, "include-cluster-resources", "", "include cluster-scoped resources in the backup") f.NoOptDefVal = "true" - f = flags.VarPF(&o.DefaultRestic, "default-restic", "", "use restic by default to backup all pod volumes") + f = flags.VarPF(&o.DefaultVolumesToRestic, "default-volumes-to-restic", "", "use restic by default to backup all pod volumes") f.NoOptDefVal = "true" } @@ -299,8 +299,8 @@ func (o *CreateOptions) BuildBackup(namespace string) (*velerov1api.Backup, erro if o.IncludeClusterResources.Value != nil { backupBuilder.IncludeClusterResources(*o.IncludeClusterResources.Value) } - if o.DefaultRestic.Value != nil { - backupBuilder.DefaultRestic(*o.DefaultRestic.Value) + if o.DefaultVolumesToRestic.Value != nil { + backupBuilder.DefaultVolumesToRestic(*o.DefaultVolumesToRestic.Value) } } diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 44a2728d4..669b03e6e 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -126,7 +126,7 @@ type serverConfig struct { profilerAddress string formatFlag *logging.FormatFlag defaultResticMaintenanceFrequency time.Duration - defaultRestic bool + defaultVolumesToRestic bool } type controllerRunInfo struct { @@ -153,7 +153,7 @@ func NewCommand(f client.Factory) *cobra.Command { resourceTerminatingTimeout: defaultResourceTerminatingTimeout, formatFlag: logging.NewFormatFlag(), defaultResticMaintenanceFrequency: restic.DefaultMaintenanceFrequency, - defaultRestic: restic.DefaultRestic, + defaultVolumesToRestic: restic.DefaultVolumesToRestic, } ) @@ -215,7 +215,7 @@ func NewCommand(f client.Factory) *cobra.Command { command.Flags().DurationVar(&config.resourceTerminatingTimeout, "terminating-resource-timeout", config.resourceTerminatingTimeout, "how long to wait on persistent volumes and namespaces to terminate during a restore before timing out") command.Flags().DurationVar(&config.defaultBackupTTL, "default-backup-ttl", config.defaultBackupTTL, "how long to wait by default before backups can be garbage collected") command.Flags().DurationVar(&config.defaultResticMaintenanceFrequency, "default-restic-prune-frequency", config.defaultResticMaintenanceFrequency, "how often 'restic prune' is run for restic repositories by default") - command.Flags().BoolVar(&config.defaultRestic, "default-restic", config.defaultRestic, "backup all volumes with restic by default") + command.Flags().BoolVar(&config.defaultVolumesToRestic, "default-volumes-to-restic", config.defaultVolumesToRestic, "backup all volumes with restic by default") return command } @@ -640,7 +640,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string podexec.NewPodCommandExecutor(s.kubeClientConfig, s.kubeClient.CoreV1().RESTClient()), s.resticManager, s.config.podVolumeOperationTimeout, - s.config.defaultRestic, + s.config.defaultVolumesToRestic, ) cmd.CheckError(err) @@ -655,7 +655,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string backupTracker, s.sharedInformerFactory.Velero().V1().BackupStorageLocations().Lister(), s.config.defaultBackupLocation, - s.config.defaultRestic, + s.config.defaultVolumesToRestic, s.config.defaultBackupTTL, s.sharedInformerFactory.Velero().V1().VolumeSnapshotLocations().Lister(), defaultVolumeSnapshotLocations, diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index c9edf49f0..79d8017b1 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -71,7 +71,7 @@ type backupController struct { backupTracker BackupTracker backupLocationLister velerov1listers.BackupStorageLocationLister defaultBackupLocation string - defaultRestic bool + defaultVolumesToRestic bool defaultBackupTTL time.Duration snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister defaultSnapshotLocations map[string]string @@ -93,7 +93,7 @@ func NewBackupController( backupTracker BackupTracker, backupLocationLister velerov1listers.BackupStorageLocationLister, defaultBackupLocation string, - defaultRestic bool, + defaultVolumesToRestic bool, defaultBackupTTL time.Duration, volumeSnapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, defaultSnapshotLocations map[string]string, @@ -122,7 +122,7 @@ func NewBackupController( volumeSnapshotLister: volumeSnapshotLister, volumeSnapshotContentLister: volumeSnapshotContentLister, newBackupStore: persistence.NewObjectBackupStore, - defaultRestic: defaultRestic, + defaultVolumesToRestic: defaultVolumesToRestic, } c.syncHandler = c.processBackup @@ -342,8 +342,8 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg request.Spec.StorageLocation = c.defaultBackupLocation } - if request.Spec.DefaultRestic == nil { - request.Spec.DefaultRestic = &c.defaultRestic + if request.Spec.DefaultVolumesToRestic == nil { + request.Spec.DefaultVolumesToRestic = &c.defaultVolumesToRestic } // add the storage location as a label for easy filtering later. diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 9f6561ab7..7aa3a1ef0 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -340,20 +340,20 @@ func TestProcessBackupCompletions(t *testing.T) { timestamp := metav1.NewTime(now) tests := []struct { - name string - backup *velerov1api.Backup - backupLocation *velerov1api.BackupStorageLocation - defaultRestic bool - expectedResult *velerov1api.Backup - backupExists bool - existenceCheckError error + name string + backup *velerov1api.Backup + backupLocation *velerov1api.BackupStorageLocation + defaultVolumesToRestic bool + expectedResult *velerov1api.Backup + backupExists bool + existenceCheckError error }{ // Completed { - name: "backup with no backup location gets the default", - backup: defaultBackup().Result(), - backupLocation: defaultBackupLocation, - defaultRestic: true, + name: "backup with no backup location gets the default", + backup: defaultBackup().Result(), + backupLocation: defaultBackupLocation, + defaultVolumesToRestic: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -372,8 +372,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultRestic: boolptr.True(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToRestic: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -386,10 +386,10 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup with a specific backup location keeps it", - backup: defaultBackup().StorageLocation("alt-loc").Result(), - backupLocation: builder.ForBackupStorageLocation("velero", "alt-loc").Bucket("store-1").Result(), - defaultRestic: false, + name: "backup with a specific backup location keeps it", + backup: defaultBackup().StorageLocation("alt-loc").Result(), + backupLocation: builder.ForBackupStorageLocation("velero", "alt-loc").Bucket("store-1").Result(), + defaultVolumesToRestic: false, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -408,8 +408,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: "alt-loc", - DefaultRestic: boolptr.False(), + StorageLocation: "alt-loc", + DefaultVolumesToRestic: boolptr.False(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -428,7 +428,7 @@ func TestProcessBackupCompletions(t *testing.T) { Bucket("store-1"). AccessMode(velerov1api.BackupStorageLocationAccessModeReadWrite). Result(), - defaultRestic: true, + defaultVolumesToRestic: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -447,8 +447,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: "read-write", - DefaultRestic: boolptr.True(), + StorageLocation: "read-write", + DefaultVolumesToRestic: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -461,10 +461,10 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup with a TTL has expiration set", - backup: defaultBackup().TTL(10 * time.Minute).Result(), - backupLocation: defaultBackupLocation, - defaultRestic: false, + name: "backup with a TTL has expiration set", + backup: defaultBackup().TTL(10 * time.Minute).Result(), + backupLocation: defaultBackupLocation, + defaultVolumesToRestic: false, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -483,9 +483,9 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - TTL: metav1.Duration{Duration: 10 * time.Minute}, - StorageLocation: defaultBackupLocation.Name, - DefaultRestic: boolptr.False(), + TTL: metav1.Duration{Duration: 10 * time.Minute}, + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToRestic: boolptr.False(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -498,11 +498,11 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup without an existing backup will succeed", - backupExists: false, - backup: defaultBackup().Result(), - backupLocation: defaultBackupLocation, - defaultRestic: true, + name: "backup without an existing backup will succeed", + backupExists: false, + backup: defaultBackup().Result(), + backupLocation: defaultBackupLocation, + defaultVolumesToRestic: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -521,8 +521,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultRestic: boolptr.True(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToRestic: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -535,12 +535,12 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup specifying a false value for 'DefaultRestic' keeps it", + name: "backup specifying a false value for 'DefaultVolumesToRestic' keeps it", backupExists: false, - backup: defaultBackup().DefaultRestic(false).Result(), + backup: defaultBackup().DefaultVolumesToRestic(false).Result(), backupLocation: defaultBackupLocation, // value set in the controller is different from that specified in the backup - defaultRestic: true, + defaultVolumesToRestic: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -559,8 +559,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultRestic: boolptr.False(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToRestic: boolptr.False(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -573,12 +573,12 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup specifying a true value for 'DefaultRestic' keeps it", + name: "backup specifying a true value for 'DefaultVolumesToRestic' keeps it", backupExists: false, - backup: defaultBackup().DefaultRestic(true).Result(), + backup: defaultBackup().DefaultVolumesToRestic(true).Result(), backupLocation: defaultBackupLocation, // value set in the controller is different from that specified in the backup - defaultRestic: false, + defaultVolumesToRestic: false, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -597,8 +597,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultRestic: boolptr.True(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToRestic: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -613,11 +613,11 @@ func TestProcessBackupCompletions(t *testing.T) { // Failed { - name: "backup with existing backup will fail", - backupExists: true, - backup: defaultBackup().Result(), - backupLocation: defaultBackupLocation, - defaultRestic: true, + name: "backup with existing backup will fail", + backupExists: true, + backup: defaultBackup().Result(), + backupLocation: defaultBackupLocation, + defaultVolumesToRestic: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -636,8 +636,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultRestic: boolptr.True(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToRestic: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseFailed, @@ -650,11 +650,11 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "error when checking if backup exists will cause backup to fail", - backup: defaultBackup().Result(), - existenceCheckError: errors.New("Backup already exists in object storage"), - backupLocation: defaultBackupLocation, - defaultRestic: true, + name: "error when checking if backup exists will cause backup to fail", + backup: defaultBackup().Result(), + existenceCheckError: errors.New("Backup already exists in object storage"), + backupLocation: defaultBackupLocation, + defaultVolumesToRestic: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -673,8 +673,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultRestic: boolptr.True(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToRestic: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseFailed, @@ -725,7 +725,7 @@ func TestProcessBackupCompletions(t *testing.T) { backupLocationLister: sharedInformers.Velero().V1().BackupStorageLocations().Lister(), snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), defaultBackupLocation: defaultBackupLocation.Name, - defaultRestic: test.defaultRestic, + defaultVolumesToRestic: test.defaultVolumesToRestic, backupTracker: NewBackupTracker(), metrics: metrics.NewServerMetrics(), clock: clock.NewFakeClock(now), diff --git a/pkg/generated/crds/crds.go b/pkg/generated/crds/crds.go index c076ed81e..3cd478009 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\xecp\x8e\x0e>$\xd9o\x98\xe1\xa6\xc8s\xbeT\x11\xeaa\xb1\xf5^\xcc\xf9\xfe\xb9\x1dP\ng\\!8A\xf7\xbftT\xb1\xd2\xfe\x1f\x94B\xda\xd9\x1d\xfa\x99oG\x14vfƬP{\x11\x82/\x1d\x904\x8fB\x9d&\u007f\a\xc82d5P\x85c\xd8\xecz\x9e\xc65\xbc\x1e\x8c\v\xa7\xe2N\xa2\xcaANyZ\xd4._\xf0\xfd\xf2\xba\xb7\xc7/7\xfa2\x1cϽ\x1d\x9b\xce\xf2\x19\xc0F\xabw\xb8䙗\xdf\xee\xba,Һ\x05\x83\xf8\xaal\x993K\xd1\\:\xc5iZ}\xdfB\xae\xe88\xb6\vt\xae4\xce/D\xe2\xc18\x1f2t\x1d\xe7q 74\x1d\xd3Ĝ\x10\x88]\xb8\xe326\xddf\x90!;IU\x92\x94\x1c\x0e&8{\x10\xf3\bR(\x05\x97\xcd\x1e\r\xf6\xf12\\q\xf0\x12\"c\xb7`\x02\"\xa9BiM\x86\xceM\xa9ì\xe5\x9dI\xb8\xd5\xc96\x11\x82\x8apa0\x95\xdcKm\xa9\xdbH\xac9\xcb;\u007fk\xe5\x00ik\xd3\xff\xd3jv\x1eF\xc0\xf7\xcdE!\xf4\xeca\xd1C\xee.\xccK[!\x82\t.\xbb\xddW\xbc\x8d\x97zzQi~\xdd\x03\xb6\x90z\xc3\xc0\xe1Ӈ\x1eǐL\"\x9e\xefRߥ\x99\r\x9b뎰7K\xd3O\xb9\x0f\xb5\xd7\x03Z\xecH\xaa\x9f\x19fwN\x1b\xdf\nϗ1:\xe0q\xe5`'\xad\xf3m$\x1d_s}|\x8c\xa2\xef\xad\xfd\x86\x10\xe5\xcfa^+\x01t0\xaf\xe9V00d\x11\xc5|\r\x82 w =\xa0\xceL\xa59\x89A\x9b\x94\x17\b,\r\xc6t\xf6\x90\rm\xc9Ʀ\x86\xba*\x96\x10\xbeb\xed\x91z\"\xd7\xd1\x1e\xfc\xa3\x90S\x99\xaa\xd4\xce\x12\x93\x97\x05\x9aj\xe2PkZGLOa^\xe7:\xb7\x10o\xb2\xa8\n\x10\x051{\x11G\xe9d\x96\x05v\xe5\v\xafBz\xb6\xee\x04\x95M\xbd7\xb4)J\x85~Y4\xb0ŝ\xb1\xbc\x17\x9ḏ>2\xa3̍\x06\x01;!Ue\x17Y\xb438\xbaܳ\x8f\x9b\xfcc\x9c\xf6%ˮ\x98\xfc\xd94\xe5\"Wmʪ\x96v\xa9\xa3\xf6`\xf1#]\xa4\xd2J\xd2\x19\xf3\xb1^RT%\xa1߿\xbbI-\xde|w\x93z\xed\xbb\x9b\xd4i\xdfݤ\xefn\xd2d\xfb\xee&}w\x93~\xafn\xd24&+\xce[\r\xfe4\xb3\xfa\xec\x15\xea8b\xa3\x90\xe3\xad\xfe]\xa8\xb3^V\x97\xb7\x19\x9e3Pu\x19˷W\\]ޗss\xf5ߘ\xf9\xbaP\x8f\x94?)o(\"\x9d,\xdd[P\x887T\x9b9_^2WTҭI\xac\v;RQ\xa2IK\xf4\xa8OU\xeb\xe4f\xb6+\x18\x84R\xed\xda\x14a\x1b\xa6\xfcJ\xf5\x8a\xb3\xa5\x1f3\x05\x1f\xd3e\x9b\xe3\x1c:q\xed\xbb,\xb2\x9d\x12\xc3_\x99C\x93u\x19\xe3\xd5\x18\xf1&\x03\xbd8~Zw\u007f\xf1&\xd6f\xc0\xab\xf4\x87\x1e\x01\\4I!\x8b\u07b7\x8b#\x93Nŧ\x02\xa7\x9c\x03cAKu=X\x17S\xbf\xa2h\xb3\x13\xfe\\\x86\xa0\xe8\xac\xfd6\xe5\xda/\xa9\xdd\xf8抍nMƠ\x91=\xef\xb2ci\t\xe9\xf2\x9a\x8cn\xcd\xc5\xc8!\xb3\xa0\x12\xe3\xecJ\x8b\xf9xk\xb2\xaa\xe2\x1bj)R\x9d\xc4ԁ;QA\xb1\xc0瘯\x96\xf8\xa6\x1a\t\xbe̛\xc0\xfa\xacʈV\xd5\xc3\x04\xc8e\xf5\x10\vX2W\xfbpv\xc5\xc3i\x95\xc1\x04\x11su\x0e\xe35\f\x13@\a\xab\x1b\x96T.L\xc0\xack\x1a>\xb0^a\xa6J\xe1c*\t\u007f\xa9\xef9Vs0Si0\xe3\x99Na5SK\xb0\xbc\x82`\x86?\xdfX-P\xd7\x03\f\xaeyn\x8d@\xb7\n`\x10\xe4\xc2ʀ\x91\xbb\xffA\x90\v\xea\x01fn\xfc\a\xc1N\x1e\x8c\x13\x1a1\xfa\x93Ӣt\a\xe3\x9f\xc3c\xaaI?\xe9\xb1;v \xb8 \x1fG\xbc d\xcaTy\r\xbbO\n?\xb7|\x87\x87g6\xf2\xfc\x14&k\x1e\x02ES\x9e\x9c\x9f\xd3wB?|d\xb0ἱb\x8f?\x99\xac\xf5\xaav\x8c\xfe\xee\xd8\xce\x13\xc8(\xd4\x14ҧ:\b\x91\xde\xe2u\xa7\x0e\xf9\x8e1\xcb\x16߹5\xd1\x17aؗ\xf7\xe8\xce\xf3^M\x12\xf1\xf4\xf4S@\xdc\xcb\x02\xd7_\xaa\x10ȭJa\x1d\x12\xff\x12AaҖ\xfe<\x98\xd7\x1e\xc2\xcaDJ\u007f8\xc5\xd7\"\xe7\xf08Z\\\x8cux˗\x14,\xb1iZ\x1d\x9f\x87\xe7\xb4|іPB`cvc\xb3z\x04\xb6\x1e-\x93\xb7\x1f*Z>\xea\x89ڰq\x1e~\xe8酯\xdc\xdcSO\x1e\x94\x1ennjoe\xf9\x85Y\x00\x10\x94\xf1\xecמ1\xbd\xd5yM?%\x93\xbb\xfex~6m\xf3\x80\x14\xa7\xd5DR\xf2W\xe1\xea\x04ڀEk\x80\x85y\xec\f\x10,\xcc\x01\x8f\xa8\xc1hΗ\xf1\v\xae\xf0\xa6\xfftN?~m\xc1\x88鸪TF\xe4i\xe7\xa6w\xa6\xf1)\xf8\x13\xdb#{D{\xe5F!\xf2\xc3՝\xb1C\xe4\x9fj\xd6\xce\xd8B\xf8[ȅ\xc7\xd5\x00\xc0\x05vl@\xa58y<\xf3t\x93\x87\x84\xdd\xc1y\xe7\xf4\x966$\x9e\vtN\xecӛ\xcdW2G{\xd4t\xc8\rd\x89\xa2+\xd6$.\xbb\xef\x17CD'2O\xf1o@-\x86\xb0\xadQW\xfd=\xa7̞\"l\x1e\x18_\x87G\xfbHN(\xee\x90.\xcd$\xc1e\a\xa1\xf7\xa4@\xd6T\xfbC\xd2\xc0\x91\x93b8\x89W\x11BP\xaajO*\x1d\x13k\xbe\xb2\xba\x15\x99\xc6T[\xdeBUd/P\x95\xc37\xcf\xe1\xbb0\xf1\xb3>7\xf1-\xf0jgM\xb1\x8a\xfc\xe7\x9c\xd9u\xcc\xd8Yi\xc8e\xe2\x98&>\xc7\x1b\x01\xcbb/K\xd4 \\\xc4e\xb6\xeedJ\x90ず\x17\xd6/s\xc2\x1e;Cg\xfc/\x86\x8b\xf9\x1a\x1e\xb1\x14\xb4\xd9\xfa\x9bؚ\x02\xeeN?\xaatM1z\xfa\x82P\xf8\x00L\x10=ŵ\xfcm\x0fc1\xbc\xc1\xebA\xec8T\x1d\a\xaa\x8b\xfa\xdf\xc7wj\xbe\xa9t?\xefE=\x9f\f>\xb9H\xa1\x1d\xdc\xc0K\xbe\xcf\x1f\xe4\xae\x1f_\x94\xa5\x92\x19a\xfb\xc7_\xe9\x82\xe4\xb8\xc0\xab\xb8\x9at(\xd8{\xa8}\x03\xf8\x82\xa5Ōve\x1f\xf9\a\x85t\xde;Į\xa7r\xb5ر놈\xee\xb3\xf7X\x94\x03kMĈͤ1\xc3'Ҁ\x1e\x01\xe9\xcbU\tT\xac\x04\x18\r\n\x17\x13R\xbb\x1a\xe7\x10RO\x1a#\xc4U\x19\x19\xa0]5t\x14\xd51\xd7\aR\xf5*,\x05\xdaӻ\xe7?⠁($\xce\xff\xd88\xa4\x15\x86$\xfc\xfeN\x81Ȁ\x1d?\xe9j>\\\xf7\xa9\xf9\x8fٷ\x8a\x1f\xaa;\x86z2\xb6\x96ykkGTbO\x93 \x10Y\x86\xa4\xbb_O\xbfYwy\xc9\xff\xa4\xcf\xd2\xf1\xbf\x99\xd1\xe1,u\xb7\xf0\x9f\xffu\x011\xcf\xf4\x9c\xf0\xa0\xce\xff\x0f\x00\x00\xff\xff\xe4Y]G\xe4O\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xecwà\x18C\xe7\xf2(\xf3J(\x90\xday\xa1\xb3@\x87\xa8q:\xa5\x03\xc6\xc5\xd9\xc36\xb8\xb5\x843\xf1\xbe\xe3\xe2\x8cF0\x16\n\xf2\xe6\xfd\xa9n\x10>\x8c\x92\xbb\x15\xe4kLPC[)t\xf1E9{\xceƮ\xafG\x00\xd7R\b{\xbf\x12[T\xe0Pa\xe6\x8d\x1dbôPØ\xf7Q#\xbc\x1b\xf0V\x8d\xff%\x12ێʌ\xc2\x04x=\xc8\xec\x10\xb6e\xd2\x17\x86\x02\xb9A\xc7\xf6+\xcaR\xbd\x0f\x13\aӒ\x0ec\u0084\x9b1i̧\xb0\xfaf\u074cY?\u05cc\x19\x8f\xd7\xe5e-\xfa\xdf\x0f+\x93\xe3>[17\xbd\x85\x1f\xa9\x98\xc4DI\xa1\xf5f\aX\x94\xfe\xfd\x1a\xa4OO)\x92\x10\x9c\x18\x8e\xb2\xa7~\xf7oN\x10\xe7\xea\xf4\xe6t\xdd\a\xea\xf4/\x94B\xfd\xeaߌ\x10\xd8\xd9?F_\xbfP\x00?\xb5\xd7\\\x83\xdc\xd5\x02ȯa'\x95G{\"\x89)rʹ$~)\v\xe6w*\x1a\x85\xf0\xd9\xe1\xfe\x8d\xa2#\xd7\xd4s\x16q\xe3ti\x88)ST\xdd\xddL'\xa1\x02'\x83\xd2b\x11R\xcc'\xe6`\xf3\x84#\x9f\xcf_\xbf`>\xce\x14X\xa2a=\x12>\x9f\xa0\xd9~m\f\x91\x97\x11\x10\x83\x94:\xbb\b\xe5\x82k\x10\xf0\x82\xef!\xba\x10\x1aH \x82^C\x93g!Z\xe4\x9a\x05+\xd4\v\xbe3\x90X\x86\x98Y\xbbL\xf4a\xbc\xe0\xfb\xfc\xa4\x13\xb6\x116\xd2Ų\n\xf1\x8f\x1e0\x038\x87]\xca2\xe0\"R\xf20sD\xc1R\x17\x91F\xe2\xf6\xd9\xe4\xd5bj\xea\x1eA\x90W.\b\x85\xb4\xfd \xcbE\x04\x92\xeb\x04\x87l\x13\xa9\x88\xf4,\x94\xcc\xeb\xd7\x04\xfd\xde\xe8k\xf8j\xfcF\x8f\x05\xab\xddq\xff&]\xac\xdd}1\xe8\xbe\x1a\xcfO>\x9c\x89\x01\xe5\xb3Y\x18\x96\xb1\t\xe9\xe0\x86\x89\xfev-jV\x89\xc3\u0604\f\xab\x16\x89t\xb0єC\x04^\x85jbxٔ\xb7\uf3a2r\\l\xd2F\xafx\xb3[\x0f\xbd'\xb2x\xa1\"\xb7\xa5\xd0G\xab~ex\xdd\"\x88O\xb4/\x84ա2\xaaD\x869\xe4\x153\x91+{\xc2\xe3^fP\xa0ݏo\x04\xedQ\x92\xcf^\xf2\xfaE\xbe4\x8c\xb3\xf4i\xc9֜Ft\xc6\xf9\x1c\x1a+\xb2\xcd\xd99I\xb43\x13\aKy\xe3\x13\xe7\xe8\xe0M\x92\xe3\x86\x19n\x8a<\xe7\x83\x16\xa1\x1e\x16{\xefŜ\xef\xef\xdb\x01\xa5\xb0\xc7\x15\x82\vt\xffK[\x15+\xed\xffA)\xa4\x9d\xb5\xd0\xcf|b\xa2\xb0\xb32V\x85\xda/!\xf8\xd2\x01I\xf3(\xd4iAx\x80,C^\x03U؆ͮ\x17i\\\xc3\xeb\xc1\xb8\xb0+\xee$\xaa\x1c\xe4T\xa4E\xe3\xf2\x05\xdf/\xaf{6~\xb9їa{\xeeYl\xda\xcbg\x00\x1b\xad\xde\xe1\x92W^~{\xe8\xb2H\xeb\x16L\xe2\xe3\xb3e\xc1,esi\x17\xa7e\xf5\x19\f\x85\xa2\xe3\xd8.й\xd28\xbf\x10\x89\a\xe3|\xa8\xd0u\x82ǁ\xda\xd0tN\x13kB v\xe1\xdc\xcb\xd8t\xc2A\x8e\xec\xa4TIRr8X\xe0\xecA\xcc#H\xa1\x14\\66\x1a\xfc\xe3e8\xf6\xe0W\x88\x8cÂ\t\x88\xa4\n\xa55\x19:7\xa5\x0e\xb3\x9ew\xa6\xe0V\x17\xdbDH*\xc2!\xc2Tq/\x8d\xa5a#\xb1\xe6\xac0\xfb\xfe\xadU\x03$Ӧ\xff\xa7\xd5\xec<\x8c\x80Ϡ\x8bB\xe8\xd9͢\x87\xdc]X\x97L!\x82\t!\xbb\xddWl\xc6K#\xbd\xa84\xbf\xee\x06[H\xbda\xe0\xf0\xe9C\xb7cH.\x11\xcf\x0f\xa9\xef\xd2ʆ\xcd\xf5\x83`\x9b\xa5\xe9\x97܇\xc6\xeb\x01-v$կ\fs8\xa7\x8do\xa5\xe7\xcb\x18\x1d\xf0\xb8r\xb0\x93\xd6\xf96\x92\x8e\x0f\xb6>>G\xd1\xf7\xd6~C\x8a\xf2簮U\x00:\x98\xd7tR8r874\xf8\x18\x04A\xee@z@\x9d\x99Js\x11\x83\x8c\x94_\x10X\x1a\x9c\xe9\xec&\x1b\xc6\x12æ\x81\xba*\x96\x10\xbeb\xed\x91z\xa2\xd6ў\xfc\xa3\x90S\x95\xaa4\xce\x12\x93\x97\x05\x9ajbSkFGLOa]爷\x10o\xb2\xa8\n\x10\x051{\x11Gig\x96\x05v\xe5\v\xafBz\xf6\xee\x04\x95]\xbd7d\x14\xa5B\xbf,\x1b\xd8\xe2\xceX\xb6E's\xac\xb7\xcc(s\xa3A\xc0NHU\xd9E\x1e\xed\f\x8e.\x8f죑\u007fLо\xe4\xb5+&\u007f\xb6L\xb9(T\x9b\xf2\xaa\xa5]\x1a\xa8=X\xfc\xc8\x10\xa9\xb4\x92t\xc6|l\x94\x14UI\xe8\xf7\xefaR\x8b7\xdfä\xde\xf8\x1e&u\xc6\xf70\xe9{\x9849\xbe\x87I\xdfä\xdfk\x984\x8dɊ\xebV\x83?ͼ}\xf6\bu\x1c\xb1Q\xc8\xf1T\xff.\xf4^/\xeb\xcb\xdb\f\xaf\x19軌-\xdd+\xee8\xef˹9\xfao\xdc|ݨGʟ\x9474\x96N\xb6\xee-h\xc4\x1b\xea͜o/\x99k*\xe9\xf6$֍\x1d\xa9)ѤW\xf4\xa8O\x9d\xec\x14f\xb6;\x18\x84R\xed\xde\x14a\x1b\xa6\xfcJ\xfd\x8a\xb3\xad\x1f3\r\x1f\xd3m\x9b\xe3\x1c:\t\xed\xbb,\xb2\x9d\x16\xc3_\x99C\x93}\x19\xe3\xdd\x18\xf1$\x03\xbd8~Zw\u007f\xf1&\xf6f\xc0\xab\xf4\x87\x1e\x01\xdc4I)\x8b\u07b7\x9b#\x93N\xc5\xeb\x03\xa7\x9c\x03cAKu=\xd8\x17S߬h\xb3\x13\xfe\\\x86\xa4\xe8,{\x9b\n\xed\x97\xf4n|s\xc7F\xb7'c\xd0ɞwر\xb4\x85tyOF\xb7\xe7bd\x93YЉqv\xa7\xc5|\xbe5\xd9U\xf1\r\xbd\x14\xa9ObjÝ\xe8\xa0X\x10s\xccwK|S\x8f\x04\x1f\xe6M`}VgD\xab\xeba\x02\xe4\xb2~\x88\x05,\x99\xeb}8\xbb\xe3\xe1\xb4\xcb`\x82\x88\xb9>\x87\xf1\x1e\x86\t\xa0\x83\xdd\rK:\x17&`\xd6=\r\x1fد0ӥ\xf01\x9d\x84\xbf4\xf6\x1c\xeb9\x98\xe94\x98\x89L\xa7\xb0\x9a\xe9%X\xdeA0ßo\xec\x16\xa8\xfb\x01\x06\xdfyn\x8f@\xb7\v`\x10\xe4\xc2\u0380\x91\xb3\xffA\x90\v\xfa\x01fN\xfc\a\xc1Nn\x8c\x13\x1a1\xfa\x93Ӣt\a\x93.lM\xc6I\x8fݹ\x03\xc9E\xba\xae\x95)S\xe55\xec>)|\x05\xf3\x1d\x1e\x9e\xd9\xc9\xf3U\x98\xac\xb9\b\x14]y\n~N\xef\t\xfd\xf0\x91Ɇ\xf3Ɗ=\xfed\xb2\xd6M\xdb1\xfa\xbbs;\xd7\"\xa3PSJ\x9f\xfa D\xba\x9f\xd7]:\x14;\xc6*[\xbc\xe7\xd6d_\x84a_ޣ\x96罚$\xe2\xe9駀\xb8\x97\x05\xae\xbfT!\x91[\x95\xc2:$\xfe%\x82¢-\xfdy0\xaf=\x84\x95\x89\x94\xfep\x8a\xafE\xae\xe1q\xb6\xb8\x18\xebp\x97/)XbӴ:>\x0f\xafiŢ-\xa1\x84\xc4\xc6\xec\xc6V\xf5\bl]d\xa6h?t\xb4|\xd4\x15\xb5a\xe7<|\xf9\xd3\v_\xb9\xb9\xeb\x9f<)]\xe6\x8e\x15\xdf\xca\xf2\r\xb3\x00 (\xe3\xd97@cy\xabs\xc3~J&w\xfd\xf9|\x95\xda\xe6\x01).\xab\u05579_\x85\xab\vh\x03\x1e\xad\x01\x16\xd6q0@\xb00\a<\xa2\x06\xa3\xb9^\xc67\xb8\xc2=\xff\xd35\xfd\xfc\xb5\x05#\x96\xe3\xaaR\x19\x91'\xcbM\xf7L\xe3\xf5\xf0'\xf6G\xf6\x88\xf6ʍB䫪;c\x87\xc8?լ\x9d\xb1\x85\xf0\xb7\x90\v\x8f\xab\x01\x80\v\xfc\u0600Jq\xf1x\xe6\xea&O\t\xd6\xc1u\xe7t\x976\x14\x9e\vtN\xecӝ\xcdWrG{Դ\xc9\rT\x89b(\xd6\x14.\xbb\xf7\x17CF'2O\xf9o@-\xa6\xb0\xadYW}\x9bSfO\x196O\x8c7ƣ\u007f\x1ev$R{\xdcc7<·R\xday_~_O#\x8ep\xea\xce\x16\xde|@\x01\x95\xdcKr\x88$ؽ\xb0[\xb1\xc7Uf\x14\xe5Q\xd2\xe8S\x8c\xfe6r\rP\a\xbe\x8e\xd0#\xe8\xc7\xf6̔\bFe\x0eP\xd2\xc7\x12\xae\xe3\x8eJ\x12,\xc4_\x8d\xed\x1f\xd6\x14R\x1b\x1b\xc2\x17\x0e\xa1\xd3\xd2\xc5\xfe\x9c\xaf\xb5N\xe2\xfb@3\xeaө\x96\xaf¤L\xc3\xfb\xfc\xd0)\xc6\n\xbe\xe2\xe9\x16\x15\x0e&0\u007f\xae\xbf\xa2ћ\xb0\xd1\x0f\xd6\xec)\xe2\xeb\xfdt\x97\xbcR\xef\x97\aa\xbd\x14J\xbd\a\xf0#o\xed=\xfe\x82\xe4\x17F6\x82!\x06F̦y\x18'5!\xa5\xd4A\xd6|\x98\xb05\x95\xef\x18\\c\xb0=\x89\xa7\xf7\xad)O\xc4T7\x90]\x88\xb4\x03\xa2\xf3+\xdc\xed\x8c\xf5!~]\xad@\xee\xe2\xc6҃Jޙ+_\xe1k\f }\x93\xc55\xbaɱ\xa0E\xe1X7=\u007f\x11\x82\xcb\xce\"\xcb(>\xc1\x1b\xe7\x85\xea\xf9\x80o.w\xf1~Mڅ\xf9_z\xdbY\x8fɛ\xf6\xec\xbaϵ*\xb6hIS\x19X\xe0\x17\x1f\xed\x05\xaf\xa7\x86S\xba-\xa2\x86W+\xbd'\u007f\xd3.\b\x82'\x0f\xa3\x148\x03;1xgx\xdc\xe7\xf1\xaf\xc6\v\xb5\x19Kh\xbb!`=5\x91Ë\xfbD\x19\x12ÖI\x1f$'4wH\x97V\x92ಃ\xd0{R k\xaa\xfd!i\xe0\xc8N1\\ī\b!(U\xb5'\x95\x8e\x855_Y\xdd\xcaLc\xa9-o\xa1*\xb2\x17\xa8\xca\xe1\x93\xe7\xf0\xad\x98\xf8\xa9\x9f\x9bx\x17x\xb5\xb3\xa6XE\xfes\xcd\xec:V\xec\xac4\x142qN\x13\xaf㍀e\xb1\x97%j\x10.\xe22\xdbw2%\xc8\xf1D\xcd\v\xeb\x97\x05a\x8f\x9d\xa93\xf1\x17\xc3\xc5|\r\x8fX\n2\xb6\xbe\x11[S\xc0\xdd釖\xae)GO_\x15\n\x1f\x85\t\xa2\xa7\xbc\x96\xbf\xeea,\x86;x=\x88\x9d\x80\xaa\x13@uQ\xff\xfb\xc4N\xcdw\x96\xee磨\xe7\x93\xc9'\a)d\xc1\r\xbc\x14\xfb\xfcA\xee\xfa\xf9EY*\x99\x11\xb6\u007f\xfc\x95\x0eH\x8e\v\xa2\x8a\xabɀ\x82\xa3\x87:6\x80/XZ\xcc\xc8*\xfb\xc8?(\xa4\xfd\xde!v#\x95\xabŁ]7Et\x9f\xbdǢ\x1cx\xd7D\x8e\xd8,\x1as|\"M\xe8\x11\x90\xbef\x95@\xc5N\x80Ѥp1!u\xa8q\x0e!\xf5\xa21B\\\x95\x91\x03\xdaUC[Q\x9ds} U\xaf\xc2R\xa2=m=\xff\x11'\rd!q\xfd\xc7\xe6!\xad4$\xe1\xf7wJD\x06\xfc\xf8ɣ\xe6cv\x9f\x9a\xff\x98}\xab\xf8\xf1\xbac\xe8'co\x99\xb7L;\xa2\x12\x9f4\x05\x02\x91eH\xba\xfb\xf5\xf4;v\x97\x97\xfcO\xfaT\x1d\xff\x9b\x19\x1d\xf6Rw\v\xff\xf9_\x17\x10\xebL\xcf\t\x0fz\xf8\xff\x01\x00\x00\xff\xff\xff\x8cC\xfd\xf8O\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"), @@ -37,7 +37,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y_s۸\x11\u007fק\xd8\xf1=\xb87\x13RMz\xd3\xe9\xe8\xed\xcen:n\xef\x1cO\xe4\xcbK&\x0f\x10\xb1\x14Q\x93\x00\x8b]HV;\xfd\xee\x9d\x05HI\x94(Y\xce\xf4rzI\b,\x16\xbf\xfd\xed\x1f,\xe0I\x96e\x13՚O\xe8\xc98;\x03\xd5\x1a|f\xb4\xf2E\xf9\xd3_(7n\xbaz\xbb@Vo'O\xc6\xea\x19\xdc\x04b\xd7|Dr\xc1\x17x\x8b\xa5\xb1\x86\x8d\xb3\x93\x06Yi\xc5j6\x01P\xd6:V2L\xf2\tP8\xcb\xde\xd55\xfal\x896\u007f\n\v\\\x04Sk\xf4q\x87~\xff\xd5\x1f\xf3w\xf9\x0f\x13\x80\xc2c\\\xfeh\x1a$VM;\x03\x1b\xeaz\x02`U\x833h\x9d^\xb9:4\xe8\x91\xd8y\xa4|\x855z\x97\x1b7\xa1\x16\v\xd9u\xe9]hg\xb0\x9bH\x8b;Dɚ\a\xa7?E=\x1f\x93\x9e8U\x1b\xe2\u007f\x8cN\xffl\x88\xa3H[\a\xaf\xea\x11\x1cq\x96\x8c]\x86Z\xf9\xe3\xf9\t@\xeb\x91Я\xf0W\xfbd\xddھ7Xk\x9aA\xa9j\x92i*\\\x8b3\xb8\x17\xa4\xad*PO\x00V\xaa6:\U00091c3b\x16\xed\x8f\x0fw\x9f\xfe4/*lT\x1a\x14ͮEϦ7Q~{\xdeݎ\x01h\xa4\u009b6j\x84kQ\x95d@\x8b?\x91\x80+\x84\xce+\xa8\x81\xe26\xe0J\xe0\xca\x10x\x8c6\xd8\xe4\xe1=\xb5 \"ʂ[\xfc\x13\v\xcea.vz\x02\xaa\\\xa8\xb5\x04\xc1\n=\x83\xc7\xc2-\xad\xf9\xf7V3\x01\xbb\xb8e\xad\x18;\x86\xfb\x9f\xb1\x8cުZH\b\xf8\x06\x94\xd5Ш\rx\x94= \xd8=mQ\x84r\xf8\xc5y\x04cK7\x83\x8a\xb9\xa5\xd9t\xba4\xdc\xc7s\xe1\x9a&XÛi\x8cJ\xb3\b\xec\xdc\xf1,!\xf2+\xa5\xca?(\xae^\xdc\xf5\xfa\xaeL\xdbĊ\xc0\x0e\x14\xb4\x06\v\x1c\x94V0\x96\x18\x95N\x83#*\x01$q\xbf\xfb2\xc6\x19\xc0{\xe7\x01\x9fU\xd3\xd6\xf8\x06Lby[\xd0\xfa\xf80\x94\x88\xd8ꃵ\xe1ʌ\x1b\xae$\x8e:\x83\xd7\xd1PVO\b\xae34 \xd4\xe6\tgp%\x19\xbc\a\xf1?\x92:\xff\xbd\x1a\xd5\xf9\x87\x94\"W\"r\x95\x80mϬ\xfd\x8c\xdb\x01\xe4J1\xb07\xcb%z\x1cg3\x16b)p߃\xf3b\xbbu{\n\xa2Z\xf1Y\xaa3\xa8\x8f\x00\u007f~\xf7\xe5\x04\xda!O`\xac\xc6gx\a\xc6&VZ\xa7\xbf\xcf\xe11F\xc4Ʋz\x96}\x8a\xca\x11Zp\xb6ތ\xa3uP\xa9\x15\x02\xb9\x06a\x8du\x9d\xa5^A\xc3Zm\xc4\xfe\xde]\x12a\nZ\xe5y\xd8\r\x8cj}\xfcp\xfba\x96PI\b-c\x1d\x93S\xa64r\xe6\xcba\x9fN.\x89\xc9HGH\xc1\xc1\x0e\x8aJّ\xb2\x06\xb1i\x88\xec\x96AΒ\xfc\xfa\xb5\xd9zxl\xf7\xbf\x91\xe3\xfb\xb00\xfcN\x87\xe0Ef\xc5\xd6\xf9E\xb3\xee\xf7\xe2\xf9\xacY\xd2\xc4{\x8b\x8c\xd12\xed\n\x12\xa3\nl\x99\xa6n\x85~ep=];\xffd\xec2\x93@\xccR$\xd04\xb6\xe1\xd3\xef\xe2?_eE\xec\x8c/3%\x8a~\v{d\x1f\x9a\xbeڜ\xbe\xaf\xbb\xf4T\xba\x9ew\x8d\xc7\xe1JI\x89ue\x8a\xaao\xd2w\xd5s4G\x1a\xa5S\xc9Uv\U000db1ed\x10\x19\xbc\xe0\xd9d\xdd]0SV\xcb\xff\xc9\x10\xcb\xf8\xab\x99\v\xe6\x82$\xfd\xf5\xee\xf6\xdb\x04s0\xaf\xce\xc8ц4\xc5D\xeb\xee\xb4\xd0W\x1a\xf4g\xbb\xa9\x8f\x03Ѿ\v\x1c\xe9\xe3\xb62\x177rdUK\x95\xe3\xbb۳\b\xe6[\xb1~\xf7\x1d\xe5]\xfb\xd6k\x92\x10=ӷ\x9dD\x92ԜE\x91\xfa\xee\xb1.\xb8Ð:\x868\"\x1d\xe8W!\x91됴9\xfbH\xb2\xf1\x0e~ \xd1:=\xf8\x1e\xfaw0\xb5#}0\x9c\x8cx\xf12Ê\x03]~\x9d\x89\xe2=g)?\xb9S\x12\xcf\uebfa\xd0\x14N\x9a\xb9\xe1\xe3\xcd9\xcf\xdd\x1c\xcb\xc7\x17\x02\xaf\x13.6\r\xc6\xdbBD\x00kE\xfd\x16\xc7~\x83=mia\xac\x84\xa2\ful\xb6\xa4\x0f,\x95\xa9Q\xc3\xf6\xe9\b\x1e\xe5>\x17\xaf\xcc\xd7ǵ\xb2W\x13\bu\xbc\xe7\x8d\x00>\\U:\xdf(\x9e\x81\\\x933Qp0oC]\xabE\x8d3`\x1f\x0e'O\xa6A\x83Djy>\x0f~I2\xe9\x86\xd5-\x00\xb5p\x81\xb7W\xac.!:\xf3\xaf\xa9\xf3\xf8\xe5\x17\xbcJ\xd1y\x10\x0f\"1\x16Wۤ<\x17X\x10o/\xa19\xdc\"\x83{\\\x1f\x8d\xdd\xd9\a\xef\x96\x1e\xe9\xd0\aY﨣\xf6;\x83\xf71\x02.6\xb8\xdb\xe0\xbc͝\x10T\xae\xee#ױ\xaa\xc1\x86f\x81^\f_l\x18\xa9g\xa0O\xf4\xe3\x1bj\xecyw\xbc\xed\xd6\xf7\xd5*)\xea:\xf8B\xd9\xf8$#\xd1\xc9\x0e\xb4\xa1\xb6V\xc7-|oC<\xf6$8%Cvq\xd1g\x97\xa4t\x9c{͝:¹uv\xb4#\xebS\xc1X\xfe\xf3\x0f'\xcfGc\x19\x97\x83R\xd8\xcd\n\x85?\x89\xfe\xff\xb7\ue4c7/\xb1\xf2|Y\xe9\x9a\x0fD_\xaaZQ\xf1X\xcd\xda/?\xc7\xe5f\xb8ɷ\xa84#\xd4\x1c\f\xed\x1e\xec\xdf\uefa2\x8b\xb2\xee\x81>N@2K\xefm\xde=Fu#\xbb\x03K\x15\xd2k\xa1\xbe?|\xa1\xbf\xba\x1a<\xb8\xc7\xcf\xc2Ym\xd2_\x17\xe0\xf3\x97\ttOT\x9fz\x1c2\xf8\xbf\x00\x00\x00\xff\xff6\x10(\x86\xdc\x18\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4W\xcdn\xe36\x10\xbe\xfb)\x06\xdb\xc3^*\xb9i{(tk\xd3.\x104\t\x16\xce6\x97\xa2\a\x8a\x1a\xd9\xd3P$\xcb\x19:u\x9f\xbe %ٲ-{\x83\x05V7\x0e\x873\xdf|\xf3CjQ\x14\xc5Byz\xc6\xc0\xe4l\x05\xca\x13\xfe+hӊ˗\x9f\xb8$\xb7\xdc\xde\xd4(\xeaf\xf1B\xb6\xa9\xe06\xb2\xb8n\x85\xecb\xd0\xf8+\xb6dI\xc8\xd9E\x87\xa2\x1a%\xaaZ\x00(k\x9d\xa8$\xe6\xb4\x04\xd0\xceJp\xc6`(\xd6h˗Xc\x1d\xc94\x18\xb2\x87\xd1\xff\xf6\xbb\xf2\xfb\xf2\xc7\x05\x80\x0e\x98\x8f\u007f\xa2\x0eYT\xe7+\xb0ј\x05\x80U\x1dV\x10\x90\x85t@\xef\x98\xc4\x05B.\xb7h0\xb8\x92܂=\xea\xe4v\x1d\\\xf4\x15\x1c6\xfa\xd3\x03\xa4>\x9cU6\xb4\x1a\r\xed\xf2\x96!\x96\xdfg\xb7\xef\x89%\xabx\x13\x832s@\xf26\x93]G\xa3\u0099Br\xe0\x032\x86-\xfea_\xac{\xb5\x1f\bM\xc3\x15\xb4\xca0.\x00X;\x8f\x15<&\xa8^il\x16\x00[e\xa8Ɍ\xf4\xe0\x9dG\xfb\xf3ǻ\xe7\x1f\x9e\xf4\x06;\xd5\v\x93e\xe71\b\x8d1\xa6o\x92߽\f\xa0Aց|\xb6\b\uf4e9^\a\x9a\x94Qd\x90\r\u0090\x17l\x80\xb3\x1bp-Ȇ\x18\x02\xe6\x18l\x9f\xe3\x89YH*ʂ\xab\xffF-%<\xa58\x03\x03o\\4M*\x83-\x06\x81\x80ڭ-\xfd\xb7\xb7\xcc .\xbb4Jp\xa0x\xfc\xc8\n\x06\xabL\"!ⷠl\x03\x9d\xdaA\xc0\xe4\x03\xa2\x9dX\xcb*\\\u0083\v\bd[W\xc1F\xc4s\xb5\\\xaeIƊ֮\xeb\xa2%\xd9-s]R\x1d\xc5\x05^6\xb8E\xb3dZ\x17*\xe8\r\tj\x89\x01\x97\xcaS\x91\x81\xdb\\\xd0e\xd7|\x13\x86\xf2\xe7\xf7\x13\xa4\xb2Kic\td\xd7{q\xae\xb2\x8b\xbc\xa7\"\x03bPñ\x1e\xff\x81\xde$J\xac\xac~{\xfa\x04\xa3Ӝ\x82c\xce3ۇc| >\x11E\xb6\xc5\xd0'\xae\r\xae\xcb\x16\xd16ޑ\x95\xbcІ\xd0\x1e\x93α\xeeHR\xa6\xff\x89Ȓ\xf2S\xc2m\xeek\xa8\x11\xa2o\x94`S\u009d\x85[ա\xb9U\x8c_\x9d\xf6\xc40\x17\x89\xd2\xcf\x13?\x1dGNJ=[{\xf18-f3t\xda\xffO\x1euJXb-\x1d\xa4\x96t\xee\x01h]\x00u\xa6_N\f\xcf5g\xfaj\xa5_\xa2\u007f\x12\x17\xd4\x1a\uf75e\xb4\xf9\x05T\xbf̝\x18a\xa5\x11\xd77*\xce+\x9eX\x06\x90\x8d\x92I\x87\x8a\"\xbbo\xf3\x998.R\x9eiW\xa9]\xad\xb2\x1a?\xe4ڱzw5\x96\x87\x99\x03)\x94\x8d{\x05\xd7\nک\xc9\x11e\x8dgA\x84h\xdf\f\xb2\x9f\xc9wM*\xad\x960\\\x05\xb8:Q\x1eyn\xa31\x83\xa5B\xbb\xce+\xa1\xda\xe0\xd8ȭ\vg\x10\xa9\xb7\xb1\xeb\xbb\xfa\xcb\xf8\xdd:\x13;\xdc\xdf\rW\x91?\x1f\xebN\v\xa4\x17\f R\b\x10\x8e\xaf\xc0\xe97\xd4\x04\x83w\xcd\x00`(ZNq\xbe\x11{J.\x05<\x9a\x86\xc5|\xf1\x1fi\xccUԑ\xc2i6\x8f6O\xf8\xfa\xec0\x10%\x91\xdf>\x0e\xb2\xfaH\xac\x8e!\xa0\x95\xc1H\xbe\t\xbfh \x18\xc52i\x8b\xf4\x06\xba\x9a\xe7\xfbs\xfd\x11R2\x05\x92\x04\xd3.zU<\xd7/\xad\v\x9d\x92\n\xd2h/ҡ\x93\xfd\xf4\x02S\xb5\xc1\n$\xc4\xd3\xcd\xcb\x13\x01\x99\xd5\xfaz\x04\x0f\xbdN\u007f\x15\x0e\a@\xd5.\xca\x05b\xf3\xa5x\x85ګ\x88\xfcF\xf1u<\x1f\x93\xc6\\Z\xf1\xad\xce\xd1\xc6\xee\xd4E\x01\x8f\xf8z&[\xa1jN{\xae\x80G's\x1b\x17b\x9a\xa9\xe5\x13\xd1\xe1\x89}sX\xe5\xba+\x86'u\xde\x00\xc8/\xd3f\x92b\xee{s\x90\x1c\x1aDi\x8d^\xb0y<}R\xbf{w\xf4B\xceK\xedlC\xfd\xff\x00\xfc\xf9ע\xb7\x8a\xcd\xf3\x88#\t\xff\x0f\x00\x00\xff\xfft\x8f\x1aC\x8e\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xd4YMo\xf3\xb8\xf1\xbf\xfbS\f\xf2?<\xbb@\xac\xfc\x9f\xb6\x87·4O\x16\bv7\r\x92\xa7\xe9\xa1聖\xc66\x1b\x8a\xd4\xf2ʼn[\xf4\xbb\x173$\xf5bɲ\x17\xedb[]\fS\xc3\xe1̏\xf3\xae\xc5r\xb9\\\x88F\xbe\xa2u\xd2\xe8\x15\x88F\xe2\x87GM\xff\\\xf1\xf6{WHs\xb3\xff\xbcF/>/ޤ\xaeVp\x17\x9c7\xf53:\x13l\x89_p#\xb5\xf4\xd2\xe8E\x8d^T\u008b\xd5\x02@hm\xbc\xa0eG\u007f\x01J\xa3\xbd5J\xa1]nQ\x17oa\x8d\xeb U\x85\x96O\xc8\xe7\xef\xff\xbf\xf8M\xf1\xbb\x05@i\x91\xb7\u007f\x955:/\xeaf\x05:(\xb5\x00Т\xc6\x15Xt\xdeXt\xc5\x1e\x15ZSH\xb3p\r\x96t\xd8֚Ь\xa0{\x11\xf7$A\xa2\x12\xcfq;\xaf(\xe9\xfc\xf7\xfd\xd5\x1f\xa4\xf3\xfc\xa6Q\xc1\n\xd5\x1dƋN\xeamP¶\xcb\v\x80ƢC\xbb\xc7?\xe97m\xde\xf5w\x12U\xe5V\xb0\x11\xca\xd1kW\x9a\x06W\xf0HR4\xa2\xc4j\x01\xb0\x17JV\xacb\x94\xcb4\xa8o\x9f\x1e^\u007f\xfbR\xee\xb0\x16q\x11\xa0BWZ\xd90]\x96\x0f\xa4\x03\x01\xaf\xac\x1f\t\xc1\x17\x01~')\x0eX7\xfep\rB\xa9\xbe\x03\n\xdb\x01\xf8\xeb\x1aԅ\x8e\xf5pL}ƱN#46\x8e>F\x9d\xa5%\xba\x14Y\xff\v\x00Sb\x8d\xea\x05\x15\x96\xde\xd8Y\xb0~\xe8SF\xa0(/\xee?\x17\xc37\xde\xc0F*\x8f\x16ޥߍ\x14xߡN8QE\"u%\xf7\xb2\nB\r\xac\xac\x87R\a&\x18\vZ\xaa\xeb\x11O\xc28\xef\x1e`\n\u007fd\xe1\x85\xfaY>x*\xc5\xd2S\v_\xee\xee?\xa88r]\xf73\x03\xdb\xf1\x86\x88\\N_\f?\xb8\x8c\x1dUH\xd2b\xcdu\xd7\x04g\x80\xafle\x1d\x15\xeb{\xfb\xf8el@pڈFB\xde\xce\b\x92|\xa2\xbd^\xca.9\x11Or\x86T9_\x83\x807<\xc4\"\x9b\xea\xf8\x86Bifa\x91\xcbs\xbe\xe87<0Q\xaa\xb8'\xb9\xce]J|\xde\xf0p\xeaՑ\xbat^\xaa~\xa2\u07b4\xc0Rq=\x96U\xe5\xee\nO)I\x8f7\xd3\xc2¼\xa7\xe6'#r\xa1\xd8-\x80\xbd.\x90!\xfe\xe4\"\x9cd_;\x19\x1b\xbc\x19\xa9\x1d\xb2\xed\xe5\xfe\xe6\x95:Ֆy\xb4\xa8\a}\r\x8f\xc6\xd3\xcf\xfd\x87\xa4\x9a]\xe8q\x06\xed\x9e/\x06ݣ\xf1L\xfboA\x12\x85\xba\x10\x90H\xcc\x06\xaacl#\xbd\xfa\xed\x8f\xe3\xe8A\xb7\x9a\xf5\x9bQB:jA\x8c͚s\xdb\x1a\x8f\x88\xcc\xeb\xe0\xb8c\xd1F/9\"e\xee3L\xdbK\x93.Ci\xec\x00\xaf\x13\a\xcd\xf0\\#\xa4\xe3\xbfR#\x16\xf7\xc4VZ\x89\x12+\xa8\x02C\xc0\xad\xa0\xf0\xb8\x95%\xd4h\xb7sr6\x14\xa7N_\xddL$\x89\xcf\x05w{:\v\xe5'\x85\x9dj\xfa\xa0%\xd9\xfa\x897\xb3\xd7;٫]&\x15\x87oNp\x93ڋ\xaa\x921\xc3<\x9d\x89Og\xf0\x19\xe7\x8cxhJ\xb4\xa2!\xcb\xfe\a\x85S6\x94\u007fB#\xa4u\x05\xdc\xf2\xbcJM\xdfl\x9f>U\x1e}\xd6\xc4U: \xcc\xf7BQ\xa8\xa7\xc0\xa1\x01\x15\a\xfeI\x96f3\xcah\xd7\xf0\xbe3.F\xf1\x8dD\xc5S\x88\xab7<\\]\x0f<\x0f\xe4t(\xbdz\xd0W1I\x8c\xfc\xa0m\xf8\x8cV\a\xb8\xe2wW\xc5(\tN\xb2\x9dM\x8c3\x16q\xf2U[\xe9\xfe(\x9aF\xea\xed\xf1=_f\v3v0\xb0\x81ǣ\xd3\x06\x86\xd0/K\a%\xfc\xf8\xb88\xe6\x9b(\xf6m\x1e6jo\n\xb8Շ\x11WG\x1d\xe3D\xa9;\xec I\xa4w\xa9\x14E\xa5ijb\xa6}Fi\xb0\xe0D\x1d\xf9O\x0f\r&@O\x1c\x9f^\xe7+\xf9\xe7\x96l\xa2\x0f\xec)K\x95b\xab\xc0\xd3\xeb\xd8r\xb8\xf8tZ4ng<|\xb3\x97\"M\xb5L\xa8\x1ak\xf6\xd4\x0f~\xfb\x1f\xea\xe8\\\xb9\xc3*(<;\xb4y\xe9\x11\x9e\x1f\xdbd\xb6c[\xe8ph;\xb9\x8cV\x15=p8\x1eJ-L\xe2K\x97<\xd5D\xb7\f\xa39\x18\xc73Y\n'.\x94%:\xb7\t*w<<\xf6\xa7\xf6:\x92K\xd7J{\xe1\x18i*C,{ï\xb3\x13:/|pggtL\x05\xa5h|\xb0\xa9H-\x83\xb5\xacT|g6\xa31\xdd\x05S:\xb4\xd6\xd83\xd3\x1e&\x89\xee^\x9a\xa0\xb9V#\xbb\xe5\xbdP\xa3sb\x9b\xc7<\xefh\x11\xb6\xa8)\xc2N\x8c8R\x1d\x80\x1fX\x86\xf4\x91`اR$\x15\xa5\xa7\xf6+\x8a\xc6a\xb3u\xe2S9\x93\b\xc4\xf6ĝI\xedq\x8b\xc3L\xbc\x11R\x05\x8b\xcf(\xdc\xf0\xdb\xc1H\xfd\xef\xfa\x94\xa9\xb4\x8b\x9a\xc7\xceC\x04\x87U\x1a;{i\xf1\xe4l\x92\xfaP!G\x9d\xdf\xc9\xf8\xdb섛w\xc3'\xa2\xc8\xfe\xd77\x87\xd6\x03\x9f'eA\x1d\xeac\xc6Kx\xc4\xf7\xd1\x1a)\x8f\xd5k\xfb\xe9hD𠟬\xd9R\xe6\x1d\xbd\xba3u\xa3pl\x05Kx\x12\xd6K\xa1\xd4!\xb2?q\xea\xa58u\x1f\xb6\xee\xcf\x1b\xf3\xeb\x11\xf1р\x85̺\xe3\x97M\xf0\x1b9\x1e\xad\xa5/]k\x85\xdf\xfe:\x83\x92wa\xb5\xd4\xdbyu\xff\x9c\x88&\xbc7\xed\xff\xe5\xfc7\v8\xf4\xe0\x13\xb3\xbc\x9f\xeb\xc1\x13\xb1\xf4h\xa9\xfb\xc4\xfb\xb9\xfb\xc7h-\xd3']~Aݧ\xddc\xd5\xc3>\x89\x92V\xba\x00-\xca\x12\x1b\x9f&\x98\xfd\x8f\xbbWW\xfc'\u007f\xbd忥ѱ\xfar+\xf8\xcb_\x17\x90\x10x\xcdr\xd0\xe2\xbf\x02\x00\x00\xff\xffF\x9c\x18\xb7\x0e\x1f\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\x1c\xcbn\xe4\xb8\xf1\xee\xaf(8\x87I\x00w;\x93\xe4\x10\xf4m\xd6\xe3E\x8cLf\akǗ \a\xb6T\xedf,\x91\nI\xb5\xdd\t\xf2\xefA\x15\xa9g\xebAy\xed`\xb30O\xdd\x14Y,֛\xa5\xa2\xceV\xabՙ(\xe4=\x1a+\xb5ڀ($>;T\xf4Ϯ\x1f\xffh\xd7R_\x1e>nщ\x8fg\x8fR\xa5\x1b\xb8*\xad\xd3\xf9\x8fhui\x12\xfc\x8c;\xa9\xa4\x93Z\x9d\xe5\xe8D*\x9c\u061c\x01\b\xa5\xb4\x13\xd4m\xe9/@\xa2\x953:\xcbЬ\x1eP\xad\x1f\xcb-nK\x99\xa5hx\x85j\xfd\xc3o\u05ff[\xff\xe1\f 1\xc8\xd3\xefd\x8e։\xbc\u0600*\xb3\xec\f@\x89\x1c7`\x93=\xa6e\x86v}\xc0\f\x8d^K}f\vLh\xb5\a\xa3\xcbb\x03\xcd\x03?)`\xe2wq\x1b\xe6sW&\xad\xfbs\xa7\xfb\x8b\xb4\x8e\x1f\x15YiD\xd6Z\x8f{\xadT\x0fe&L\xd3\u007f\x06P\x18\xb4h\x0e\xf8W\xf5\xa8\xf4\x93\xfa^b\x96\xda\r\xecDf\xe9\xb1Mt\x81\x1b\xf8J\x98\x14\"\xc1\xf4\f\xe0 2\x99\xf2>=n\xba@\xf5\xe9\xdb\xcd\xfd\xef\t\x8f\\\xf8N\x80\x14mbd\xc1\xe3j\x14AZ\x10pϛ\x04\x13\xd8\x01n/\x1c\x18d\\\x94\xa3\x11\x85\xc1U\x85e\n\xda\x04\x98\x00\x05\x1a\xa9S\x99\xc0w\"y,\v?\xd5\xeeu\x99\xa5\xb0E0\xa5Z\x87\xb1\x85\xd1\x05\x1a'+\x12RkIM\xdd\xd7\xc3\xf4\x03mŏ\x81\x94\xe4\x04-\xb8=B\xe06\xa6L\xbd\\\x80ށ\xdbK\xdb\xe0\xcd$i\x81\x05\x1a\"\x14\xe8\xed?0qk\xb8%:\x1b[a\x9bhu@C\xfbN\xf4\x83\x92\xff\xaa![p\x9a\x97̄\xc3\xc0ѪI\xe5\xd0(\x91\x11\x13J\xbc\x00\xa1R\xc8\xc5\x11\f\xd2\x1aP\xaa\x164\x1eb\xd7\xf0\x17m\x10\xa4\xda\xe9\r\xec\x9d+\xec\xe6\xf2\xf2A\xbaJO\x12\x9d祒\xeex\xc9\xd2.\xb7\xa5\xd3\xc6^\xa6x\xc0\xec\xd2ʇ\x950\xc9^:L\\i\xf0R\x14rň+V\x93u\x9e\xfe\xaa\xe2\xa2\xfd\xd0\xc2\xd4\x1dIl\xac3R=\xd4\xdd,ģt'Y\xf6\xe2\xe1\xa7y\xfc\x1b\xf2R\x17Q\xe5\xc7\xebۻ\xb6\xe8Hۥ9S\xbb%M\r\xe1\x89PR\xed\xd0x\xc6\xed\x8c\xce\x19\"\xaa\xb4\xd0R9\xfe\x93d\x12U\x97\xe8\xb6\xdc\xe6\xd2\x11\xa7\xffY\xa2uğ5\\\xb1\xb5 \x99+\x8bT8L\xd7p\xa3\xe0J\xe4\x98]\t\x8boNv\xa2\xb0]\x11I\xe7\t\xdf6r݁\x9eZuwe\x8c\x069T\xe9\xf0m\x81IG5h\x96\xdcɄ\x15\x00v\xda4*n\xdb\x16k\\/y\xed0\xb4\xdb;iG\xae\x8cV\x80\xcf\xc4\xe9F_IN\x9e\xf6\xa8H\x8bL\xa9\b\xc3\x1eD\b\xc6c\xdd\xeb\x1f\xa4\x1d?\xc0\xbc e\x9cD\xed.\f\"Ԉ*i\xedd\xbc\xa9\xc0\xdad\xe9`\xa9@\x0fcW\x18}\x90i\xb0\a=\xeaMQУ\xb4\x13e\xe6~D\xebdr\xfa\xb8\x87\xf3\xe7\xf6芏h\x89~n\x8f\x86Ԍ\x1f\xd4\xf6\xb5\xb4\xec\x02N\x1b\x99,\xf1\x88\xb0\xf5{$˗eP\xe8\x14\x0e:+s\xb4\xb0=V\xc8\xf5\xe9\xde\xd0~\xabu\x86B\x9d<\xc7\xe7$+SLk?4\xb0\xf3\xde֮O\xa6\xb0;\x17R\x91\xe4\x90\xf3$$U\xf3\x94<\xc9\xe0քA U\x97\xcaC\x04\xc9l\v[\x1dڌt\x98\x0fb8!c\xbeQ\xb8 \xb6\x19n\xc0\x99\xf2T2\xaa\xf9\xc2\x18q\x1c\xa5R\x15\xe6\xc4\x13\xa9\x9e\x11\fp&\x13$\xf2\xd4f\x96\xe9\xf4\v \xd1^\xeb\xc7y\xb2\xfc\x89F5.\x04\x12\x8e\x1ea\x8b{q\x90\xda\xd8~ԁϘ\x94nD3\x84\x83T\xeevh\bR\xb1\x17\x16me\x10\xc6\xc93\xa5\xe2\xd4\xcc\x14\x8bO\xf6Ӱ\x97\x18\xc54\x18\xdb\x02\x9b\xce\x11\x98\xc0\b\x93}-\v\x90*\x95\a\x99\x96\"\x03\xa9\xac\x13*\xf1\xfb\x125nC\xfb\x82i֟`\xeeMf\x85?\xf1\xa5\xe3}\xb4B\xd0\x06rr\xb5\xa7C\xed\xe8\x1a0\xba\xfd\xad\xb0\x14pz\xf15\x14;\x87\xc5Rvl\x8d\xbd\xb8\x98\x00^s\xc7\ah\x99\xd8b\x06\x163L\x9c6cd\x99g\xbaoq\xb6p\x84\x9e\x03V\xb1\xb1\xfa\xb4\xe5f\x83\x93@\xd9\xe0?\xede\xb2\xf7\xb1\x14\xc9\x14C\x82T\xa3e[ \x8a\";\x8eo\x16\xe6%!,4m\x0e\x9a6k\x18\xfa0\x87MDӢ\xeci\xd3f,k\x97ε\x88\xbc\x93\xb9r\x1a/\x12蛓ɯ-\xd0D`I稛\x1d`^\xb8\xe3\x05HW\xf5\xceä0\xa8\xc1\xe1\x17\xc1\xa8\x97\xe8\xc3M\u007f\xee+\xeb\xc3+p\xa9F\xe1\xff\x9aI\xecln\x83\xafY\xc0\xa0/\xedy\x17 w5\x83\xd2\v\xd8\xc9\xcc!\xc7=\xd3(\xb6\\\xdf,\xa7^\x8b,q^\x93Z.\\\xb2\xbf\xae\x8f\x8b\xb3\xe3{\x14\xeaO\xf7\xb1ru\x92\xe8:\xf9Y\xc8\xc0\xd9\x04i0\xf79\x8a;ց\xa6\x87#\xb5O_?c:M(\x88\x95ȓ\xed|\xea\xa1\xdc^>\x1c\x03\xe27\x13\x02\xaa\xfa\x84\xe5sO\x17 \xe0\x11\x8f>\n\x12\n\x88Q\x82\x96\x1a=H\x9c\x12\x89\x93`l\"\x1e\xf1ȀB^+b~\xbch\xf8\xf6\x88Ǹ\x81=R\x12f\xe1\xd4\xefiJ\x1dL\x10N\x8f,!#p\x96\x925\x04\x9c\x8e\xd9$,17U\xab8\xf1\xa2\xed\xd6l\xec\xa4l\x1f\xf1\xf8\xc1z\x86\x91v\xece\x11\xbda2\xc0`\x91\xf5\xa8\xcaZދL\xa6\xf5R^\x1fn\xd4T\xd4\xddm_\xb5\xbbQ\x17p\xfd,-\xa1\xa7R\xf8\xac\xd1~Վ{ތ\xb0\x1e\xfd\x17\x91\xd5Oe\xd5S\xde\xcc\x13=\xda\xc9\xd0(\xa1\xf7\xedƟ0kVI\v7\x8a\xceJ\x81.\x9c\xd2f\x98\xf1b\xc9(\xe5\xa5嬧\xd2jŎv=\xb0V4\xcc\xc0\x1em:\xdci\xa3\xd7Z6\x1a*\x1d\xe8\x8e\xc0&\xaa\x84`\x1a\xd9\xc9UB5\xccUVZ\x87&\xbe\xe6\xf6fx\xde@\xf5u⇬\xf8\x12ΰl4\xe5\x16\x8d˪\x8bpI\xd9*E\xf1\xb5\xf9\xb3\xd1qdq\xedX\x9dv\\)\xd7\\\x01W\xb7\x06\xb9.\x9e\xaa\x8a\x90\x87\xadFX:p\xcb\xdf\xeehW\x03u\xeb\xb082\xaf\xb0\xfd\x19\xd5'G\x95X\xcd\x14VM\x97p\xebj\x8d\x11*v\x12\xf0]\xf2\x99N\x19\xf1ϐz\xb3\xb5O\xe3\x15O\xe1\x8d\x1c:q\xf8\xb8\xee>q:\xd4?\xc1\x93t\xfb\xc1M\xf1}\x13:.\xaa\x87vat%\x8b\xe1>W\x9f\xaa\xa0\r(\x99\r\xd74\x10\xc1\xab\xf9\x1dr\xc3\x0f\x85?\x94\xbeH\u007f\xe7\x8eI\xb15R/\xae\x8c\xea\xd6=\x8d\x1a\xf8\xe5/\U00016515\xc7\xd7>͕*-\xa9xjW3M\x80\x8c\xads\x8a;\xf1\xce\xd64\xbd\xa0\x92)\xba\xae\xf2U^W\xc6\xd5*\xbdE\x85҂\xba\xa4n\xbd\xd1\f\xdce\xd5H\x91d\x8a\xa9\x87iD\x15ϲڝ\b\x1a\xbe\xb0N\xa7\xae\xc2\x19]{iuN\xb7\xf6f\x14lLM\xceH\xc5\xcd(\xcc\xc9J\x9c\xd8:\x9bQ\xe8\xb3\xee{Fr&\x1f[%\n\xbb\xd7\xee\xde_5\x9d\x8d\xfcn\xbb\xe3\a\x8e^\xd5u\xd6$\xd3eZ\xc3\x1f\xde\x1e\xdf\xf2?·{v.|\xd1/i\xae@\x06\xf7Q\x85r\xfd\x1b\x92\xc37\x91\xe1\xa7\x1fŬ\xd3F<\xe0\x17\x9d\xb4>\xd50E\x93\xee\xf8\xceM\xfc\xc0\xfc*\xd9\x12\xaa\x92\x86\xc3ְ\xa3>\xb8&\xc7\x1an\b7\xe7U\xc2tX.&5\u05f9lvSww_\xfcF\x9c\xccq\xfd\xb9\xf4\xc7\xe0U!\x8cE\xa2m\xb5A?i;f \xf6\xfa\t2\x1dv\xff]\x1f\u007f\x83\x9c\xcd\xe5\xf3\xf6\xe2]\xf8\xdbѕ@V\xe4\x9a\x17\xe1\xfb\xe1y\xadȻ\xc54>\xfa\x8d\xc9\xee\x18$a\xadN\xa4\xe0;\xa1҅k\x1coq\xa1w\xdc!\x8c(\xfd\x90\x1f[\r]\x96_\xd57\xf7\xcff\x80Z'\\i\xe7?}\xc0\xc3 \x11\x85+MH\x92&\xa5\u1afd\x04\x02\xfd\r\xd8\xe5\x1f?Ȅu^\xb0&\xbf1\xf0\xa5\x1e\xd6D\xe9\xd6\xf9lk\xa5y\xf0$,\u007f]\xc0\xe7Z\xa5\xed~6\xa6\xddF\xbe/\xb0\xd3&\x17n\x03\xa9p\xb8\"ؽ瓖i\x94\xd9|\xf5yrw\xdfhD\xfdF'\x90\x95\xa7U\x17\xa6Gv2\x94\xb2_\xc1W|:\xe9\xbbV\x84x?\x97\xe6\xb3\xf2\x98\xde\xd7_\xb6\x89\xddT\xf3-\x1c~\tr\xa2\x00]\x9d\xed\r\xeeej\xe8\xc4\xdf\xc0\xf3/<,\xfcZ\x9e\x1aZ>~%\xb4\x93\xdf\xf4\x9e\x8dh\xe1\x84\x06\x0ek߀\x92\xf4\xba\x9a\xaf\x1f}l\xfe\xf1ҫ\U000358c3\u007fwȟ\x17J[\xb2\x12\xfb\x00\xac)J\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\x1cMo,9\xf1\x9e_Q\n\x87\aRf\xc2\x03\x0ehno\xf3\xb2\"\xe2\xf1\xf6i\x13rA\x1c<\xdd5\x19\x93n\xbb\xb1ݓ\f\x88\xff\x8e\xaa\xec\xfe\x9c\xfepg\x13\xb4\xac\xe2S\xe2\xb1\xcb\xe5\xfav\xb9\xdcg\xab\xd5\xeaL\x14\xf2\x1e\x8d\x95Zm@\x14\x12\x9f\x1d*\xfaϮ\x1f\xffh\xd7R_\x1e>nщ\x8fg\x8fR\xa5\x1b\xb8*\xad\xd3\xf9\x8fhui\x12\xfc\x8c;\xa9\xa4\x93Z\x9d\xe5\xe8D*\x9c\u061c\x01\b\xa5\xb4\x13\xd4m\xe9_\x80D+gt\x96\xa1Y=\xa0Z?\x96[ܖ2K\xd1\xf0\n\xd5\xfa\x87߮\u007f\xb7\xfe\xc3\x19@b\x90\xa7\xdf\xc9\x1c\xad\x13y\xb1\x01Uf\xd9\x19\x80\x129n\xc0&{L\xcb\f\xed\xfa\x80\x19\x1a\xbd\x96\xfa\xcc\x16\x98\xd0j\x0fF\x97\xc5\x06\x9a\x1f\xfc\xa4\x80\x89\xdf\xc5m\x98\xcf]\x99\xb4\xeeϝ\xee/\xd2:\xfe\xa9\xc8J#\xb2\xd6z\xdck\xa5z(3a\x9a\xfe3\x80\u00a0Es\xc0\xbf\xaaG\xa5\x9f\xd4\xf7\x12\xb3\xd4n`'2K?\xdbD\x17\xb8\x81\xaf\x84I!\x12L\xcf\x00\x0e\"\x93)\xef\xd3\xe3\xa6\vT\x9f\xbe\xdd\xdc\xff\x9e\xf0ȅ\xef\x04H\xd1&F\x16<\xaeF\x11\xa4\x05\x01\xf7\xbcI0\x81\x1d\xe0\xf6\u0081A\xc6E9\x1aQ\x18\\UX\xa6\xa0M\x80\tP\xa0\x91:\x95\t|'\x92Dz\xf0S\xed^\x97Y\n[\x04S\xaau\x18[\x18]\xa0q\xb2\"!\xb5\x96\xd4\xd4}=L?\xd0V\xfc\x18HINЂ\xdb#\x04nc\xca\xd4\xcb\x05\xe8\x1d\xb8\xbd\xb4\r\xdeL\x92\x16X\xa0!B\x81\xde\xfe\x03\x13\xb7\x86[\xa2\xb3\xb1\x15\xb6\x89V\a4\xb4\xefD?(\xf9\xaf\x1a\xb2\x05\xa7y\xc9L8\f\x1c\xad\x9aT\x0e\x8d\x12\x191\xa1\xc4\v\x10*\x85\\\x1c\xc1 \xad\x01\xa5jA\xe3!v\r\u007f\xd1\x06A\xaa\x9d\xde\xc0\u07b9\xc2n./\x1f\xa4\xab\xf4$\xd1y^*鎗,\xedr[:m\xece\x8a\a\xcc.\xad|X\t\x93\xec\xa5\xc3ĕ\x06/E!W\x8c\xb8b5Y\xe7\xe9\xaf*.\xda\x0f-Lݑ\xc4\xc6:#\xd5C\xdd\xcdBh\xcb\xc4\x163\xb0\x98a\xe2\xb4\x19#\xcb<\xd3}\x8b\xb3\x85#\xf4\x1c\xb0\x8a\x8dݧ-7\x1b\x9c\x04\nd\xf2\x9f\xf62\xd9\xfb\xf8\x8ad\x8a!A\xaaѲ-\x10E\x91\x1d\xc77\v\xf3\x92\x10\x16\x9a6\aM\x9b5\f}\x98\xc3&\xa2iQ\xf6\xb4i3\x96\xb5K\xe7ZD\xde\xc9\\9\x8d\x17\t\xf4\xcd\xc9\xe4\xd7\x16h\"\xb0\xa4\xb3\xd5\xcd\x0e0/\xdc\xf1\x02\xa4\xabz\xe7aR\x18\xd4\xe0\xf0\x8b`\xd4K\xf4\xe1\xa6?\xf7\x95\xf5\xe1\x15\xb8T\xa3\xf0\u007f\xcd$v6\xb7\xc1\xd7,`З\xf6\xbc\v\x90\xbb\x9aA\xe9\x05\xecd\xe6\x90\xe3\x9ei\x14[\xaeo\x96S\xafE\x968\xafI-\x17.\xd9_\xd7G\xc8\xd9\xf1=\n\xf5\xa7\xfbX\xb9:It\x9d\xfc,d\xe0\f\x834\x98\xfb\xbc\xc5\x1d\xeb@\xd3Ñڧ\xaf\x9f1\x9d&\x14\xc4J\xe4\xc9v>\xf5Pn/\x1f\x8e\x01\xf1\x9b\t\x01U}\xc2\xf2\xf9\xa8\v\x10\xf0\x88G\x1f\x05\t\x05\xc4(AK\x8d\x1e$N\x89ĉ16\x11\x8fxd@!\xd7\x151?^4|{\xc4c\xdc\xc0\x1e)\t\xb3\x90\t\xf04\xa5\x0e&\b\xa7L\x96\x90\x118s\xc9\x1a\x02N\xc7l\x12\x96\x98\x9b\xaaU\x9cx\xd1vk6vҸ\x8fx\xfc`=\xc3H;\xf6\xb2\x88\xde0\x19`\xb0\xc8zTe2\xefE&\xd3z)\xaf\x0f7j*\xea\uedaf\xdaݨ\v\xb8~\x96\x96\xd0S)|\xd6h\xbfj\xc7=oFX\x8f\xfe\x8b\xc8꧲\xea)o\xe6\x89\x1e\xed\x04i\x94\xd0\xfbv\xe3O\x985\xab\xa4\x85\x1bEg\xa5@\x17Ns3\xccx\xb1d\x94\xf2\xd2r&Ti\xb5bG\xbb\x1eX+\x1af`\x8f6\x1d\xee\xb4\xd1k-\x1b\r\x95\x0et\x1e\xb5;\xf2=\x1e\x82O\xdfg\"\xc1\x14Ғ\x89*\xa2!Zg\x84\xc3\a\x99@\x8e\xe6\x01\xa1 _\x10ˍh\xfb\xec\xdbb\x99\x8b\r\r\xaa\x16\f}\x1a\x83Ҋ\xf4:j\\\xc5\xfe\x88\xc1\x83\xf9\xe8\xe9\xc11{c\a\xcdqL\x04\xb5E\x9ar\xc2Vd\xdf\x16y\x89E\xdc9\x8d\x1f#\xda\tx\x97\xe5\xdb\xc0\xcbUȳ\x81\xd894`\x9d6\xd5U\x1f\x19\xc9^ژ\xb8h\xe7\x0e\x1c\xc4\xd8:{\xe7\xc1\xd2a\xee\xbc\xd1oo\u007f\xcf\xfd\x1d \xfd=\a1\xe1\x00\x86!\x17F'h\xed\x9c\xd8DY\xf8\x99\xc4f\x9d\xd4\x14\xfe\xb0\xc4wk\xb3\xc2:\x97l\xadڒP\x98ȹ\xf88q\xfd\xdc\xca˒\xf9\xa0\xff\xe7Ev9v\xc0Z\x9f\xe7BE9\xb0\x13D\xaf\xfc\xdcJ\xc5\x02(\u007fD1\x0f%\x9b\x8b%\x91k\x10\xbe\x9fO0\x90KuË\xc0\xc77\t\x1fj\xa3\x8b/;>\\U\xb3\x1b\x16\xd4\x1d×\xa4c\xad\xd0|_a\xb0\xc3\xc9Ӭ\xfe\xa2\xb0Yi\xd7N}\x10\xe4B\xa7\x1f,줱\xaeA6\x1a\xa6\xb4|I\xfavg9um\xcc\v\x8fr?\xf8\xb9\xadd\xdc^?\xd5\x17\xfa\xe3\x17\xbfC\x8d\xaf\xc7\x10\xe4\x0e\xa4\x03T\x89.\x15'\x8d\xc8\x18\xf0\"\x9e\x1d\xf1\x82\f\xb1~\xafi\xa8\xca<\x96\x10+\x96D\xa9f\xf2K\xed\t\xdf\v\x99\xbd\x15\x1b\x9d\xccQ\x973\x8e\xb9i\xddz\x04?\xb7S\xa9\x91\x8bg\x99\x979\x88\x9c\x18\x11Mr\x8a6d\x8e]\x19\x80'!\x1d{$\x82\xcc\xee\xc9\xe9h\x90\x89\u038b\f\x1d\xc2\x16wڰ\xbe[\x99b\xed\xfa\x83\\\xe8\xf1{\xc7~\x13\xb0\x132+M\xb4\xd5]ȍe'\xa4`x^\xf7\xe0\x13\x8b\u008a\xc9\x17\x95\x8e\x8e\x0ei\xe7G\u007f\x8e\xc0&\xaa\x84`\x1a\xd9\xc9UB5\xccUVZ\x87&\xbe\xe6\xf6fx\xde@\xfdu⇬\xf8aΰl4\xe5\x16\x8d˪\x8bpI\xd9*E\xf1\xf5\xfa\xb3\xd1qdq\xedX\x9dv\\)\xd7\\\x01W\xb7\x06\xb9.\x9e\xaa\x8a\x90\x87\xadFX:p˿\xf8hW\x03u\xeb\xb082\xaf\xb0\xfd\x19\xd5'G\x95X\xcd\x14VM\x97p\xebj\x8d\x11*v\x12\xf0]\xf2\x99N\x19\xf1ϐz\xb3\xb5O\xe3\x15O\xe1F\x0e\x9d8|\\w\u007fq:\xd4?\xc1\x93t\xfb\xc1M\xf1\x1b\x14:.\xaa\x87vat%\x8b\xe1\x8dW\x9f\xaa\xa0\r(\x99\r\xd74\x10\xc1\xab\xf9\x1dr\xc3\x0f\x85?\x94\xbeH\u007f\xe7\x8eI\xb15R/\xae\x8c\xea\xd6=\x8d\x1a\xf8\xe5\x17xK\xca\xca\xe3k\x9f\xe6J\x95\x96T<\xb5\xab\x99&@\xc6\xd69ŝxgk\x9a^P\xc9\x14]W\xf9*וq\xb5JoQ\xa1\xb4\xa0.\xa9[o4\x03wY5R$\x99b*\x8f\x16\xd7\x1b\x85ڞ\x99\xfdDT\x19\x8dV\x0f̀\x1e\xa8c\x9a\xaf\x19\x9a#\u007f\a\x95W\xa9\x14zA}\xd0\xeb\xd6\x11\xbfV\xd4=U\xed\x13Q\xe3\x13\x11\x97\xcfa\x1aQų\xacv'\x82\x86/\xacө\xabpF\xd7^Z\x9dӭ\xbd\x19\x05\x1bS\x933Rq3\ns\xb2\x12'\xb6\xcef\x14\xfa\xac\xfb\x9e\x91\x9cɟ\xad\x12\x85\xdd\xeb\xeaI\xebl\xe4w\xdb\x1d?p\xf4\xaa\x1e\xb4&\x99.\xd3\x1a\xfe\xf0\xf6\xf8\xe5\xff\x11\xbeݳs\xe1\x87~I\xf3\x042\xb8\x8f*\x94뿐\x1c~\x9d\f?\xfd(f\x9d6\xe2\x01\xbf\xe8\xa4\xf5\xf9\x86)\x9at\xc7w^\xe7\a\xe6WɖP\x954\x1c\xb6\x86\x1d\xf5\xc159\xd6\xf0B\xb89\xaf\x12\xa6\xc3r1\xa9\xb9\xcee\xb3\x9b\xba\xbb\xfb\xe27\xe2d\x8e\xebϥ?\x06\xaf\na,\x12m\xab\r\xfaI\xdb1\x03\xb1\xd7O\x90\xe9\xb0\xfb\xef\xfa\xf8\x1b\xe4l.\x9f\xb7\x17\xef¿\x8e\xae\x04\xb2\"\u05fc\b\xdf\x0f\xcfkE\xde-\xa6\xf1\xd1oLv\xc7 \tku\"\x05\xbf\t\x95.<\xe3x\x8b\a\xbd\xe3\x0eaD\xe9\x87\xfc\xd8j\xe8\x01\xfd\xaa~\xcd\u007f6\x03\xd4:\xe1J;\xff9\x04\x1e\x06\x89(\\iB\x924)\r?\xed%\x10\xe8_\xc0.\xff B&\xac\xf3\x825\xf9݁/\xf5\xb0&J\xb7\xceg[+̓'a\xf9\x8b\x03>\xd7*m\xf7S2\xed6\xf2́\x9d6\xb9p\x1bH\x85\xc3\x15\xc1\xee\xfd>i\x99F\x99\xcdO\x9f'w\xf7\x8dF\xd47:\x81\xac<\xadz0=\xb2\x93\xa1\x94\xfd\n\xbe\xe2\xd3Iߵ\"\xc4\xfb\xb94\x9f\x95\xc7\xf4\xbe\xfe\xdaM즚\xef\xe3\xf0%ȉ\x02tu\xb67\xb8\x97\xa9\xa1\x13\u007f\x03\xcf_xX\xf8\xb5<5\xb4|\xfcJh'\xbf\xe9\xfd6\xa2\x85\x13\x1a8\xac}\x03J\xd2\xebj\xbe\x88\xf4\xb1\xf9\x8f\x97^\x85/ \x1d\xfc\xdd!\u007fr(m\xc9J\xf0L\xa1\xa7\xd1<\x91$X\xb8\x90\tl\u007f\n\xe9\xfc\x9c\xff\xa9\xbet\xc4\xff&Z\xf9\x18\xd0n\xe0o\u007f?\x83\xe0E\xee+<\xa8\xf3\xbf\x01\x00\x00\xff\xff|l\xf6\x1b=J\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x93\xe34\x10\xbd\xfbWt-\x87\xbd`\x87\x01\x0e\x94o\xd4\x00US\xc0\xd4\xd4d\x99\v\xc5A\x91\xdaI3\xb2dԭ\f\xe1\xd7S\x92\xecI\xe28;\xbb\x87\xf5\xcd\xed\xfexz\xfd\xba\xad\xaa\xae\xebJ\r\xf4\x84\x81ɻ\x16\xd4@\xf8\xaf\xa0Ko\xdc<\xff\xc0\r\xf9\xd5\xfef\x83\xa2n\xaagr\xa6\x85\xdb\xc8\xe2\xfbGd\x1f\x83Ɵ\xb0#GB\xdeU=\x8a2JT[\x01(缨d\xe6\xf4\n\xa0\xbd\x93\xe0\xad\xc5Po\xd15\xcfq\x83\x9bH\xd6`\xc8\x15\xa6\xfa\xfbo\x9ao\x9b\xef+\x00\x1d0\x87\u007f\xa0\x1eYT?\xb4\u08b5\x15\x80S=\xb6\xc0\x18R\x90(\x89\x1c\xf0\x9f\x88,\xdc\xec\xd1b\xf0\r\xf9\x8a\aԩ\xf06\xf88\xb4p\xfcP\xe2GP\xe5@\xeb\x9cj\x9dS=\x96T\xf9\xab%\x96_\xafy\xfcF\xa3\xd7`cPv\x19Pv`r\xdbhUXt\xa9\x00\x86\x80\xf9\xc3\x1f\xee\xd9\xf9\x17\xf7\v\xa15\xdcB\xa7,c\x05\xc0\xda\x0f\xd8\xc2}B=(\x8d\xa6\x02\xd8+K&\xd3S\xce\xe1\at?>\xdc=}\xb7\xd6;\xecU1\x02\x18d\x1dh\xc8~Kg\x00bP0\"\x01\xf1\xa0\xb4Ff\xd01\x04t\x02\x05)\x90\xeb|\xe8s\xb911\x80\xda\xf8( ;\x84\xa7L\xedx\xb6ft\x18\x82\x1f0\bMD琣\xcc^m3\x8c\xef\xd3!\x8a\x0f\x98$,\xe4\\c\x94\a\x1a\xe0|@\xf0\x1dȎ\x18\x02f\xf6\x9c\x9c\xa3˜t\xa0\x1c\xf8\xcdߨ\xa5\x19O\xcf\xc0;\x1f\xadIj\xdcc\x10\b\xa8\xfd\xd6\xd1\u007f\xaf\x999ѐJZ%\x93\x0e\xa6\x87\x9c`p\xca&\xfa#~\r\xca\x19\xe8\xd5\x01\x02\xa6\x1a\x10\xddI\xb6\xec\xc2\r\xfc\xee\x03f\x02[؉\fܮV[\x92i\xb0\xb4\xef\xfb\xe8H\x0e\xab<\x1e\xb4\x89\xe2\x03\xaf\f\xeeѮ\x98\xb6\xb5\nzG\x82Zb\xc0\x95\x1a\xa8\xce\xc0]\x9e\xab\xa67_\x85q\n\xf9\xfd\tR9$\xc1\xb0\x04r\xdbWs\x96\xfaUޓ̋\x1aJX\xc1\u007f\xa47\x99\x12+\x8f?\xaf?\xc0T4\xb7\xe0\x9c\xf3\xcc\xf61\x8c\x8f\xc4'\xa2\xc8u\x18J\xe3\xba\xe0\xfb\x9c\x11\x9d\x19<\xb9\xa2%m\t\xdd9\xe9\x1c7=\tO*M\xfdi\xe06\xaf\x17\xd8 \xc4\xc1(A\xd3\xc0\x9d\x83[գ\xbdU\x8c_\x9c\xf6\xc40\u05c9ҷ\x89?݊玅\xadW\xf3\xb4\xb2\x16;\xb40\xbd\xeb\x01u\xeaY\".\xc5RG:\x8f\x01t>\x80Z\ni\xdeĐ\xbd?\vŸ#\n\x8e\xd9\xe6H3\xf8\x16\x8e\xa5U\x91\xed;\xc5xn\x9a\xa1yH\x1e\xf3ʖ:\xd4\am\xb1$(\x9b\x02\xdf\x02\x91\x1et\xb1\x9f\u05eb\xe1\x1e_.l\x0f\xc1\xa7=\x99W\xf1\xe9\xb3\xd8\u007f(\xff\x88-9\xfe\xf8i\x8aO\xfe뜮ܓU;\xa6\x81\x10\x9dK\x13\xe9]2ϒ\xc2\xf9F\x9e}%\xc1\xfe\x02\xc7\"\x92;\xd7\xf9\xfc\xd7V\xa9\xa4\x922'86u\xacQ\x10]\xa4\xbb\xd6\xd3\xf2\xccW\xd1'\x10X\x9e\xfc\xe7\xff\xfc\xc0\xb4:(\xe0B\xcd:cY0\xa7J\x17\xe6ʼn\x19\x91Ek\xd5\xc6b\v\x12\xe2<\xb2ĩ\x10\xd4\xe1\\\x15\x93\x8c\x8ew\x9c\x8f\n\xe4\xc2=i\xffe\x87\xee\x9a\xc2\xe1E\xf1RoJ\x1a\xd8\x1c\xae\x05\u07be^\xd6\xe6CRd\xd9Bں\xb5\xd0\x05K\x9f@\xc4B\x97\x8aT\x17n\a\x17$\xacO=\xa7\xd9?\x13\xfctY\x98#\xbfR|\xa1\xa93\xd3\xf1nzs|\xcb®ǻh\xfe0\x9e\u009c\x9c\x9c\xc5\a\xb5\x9d\xb88\xee\xd6t\xcd\x1a\x04\xcd\xfd\xfc&\xfa\xee\xddٕ2\xbfj\xef\f\x95\x8b4\xfc\xf9WU\xb2\xa2y\x9ap$\xe3\xff\x01\x00\x00\xff\xff\x8e\xadi\xa0\xc7\v\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V\xc1\x8e\xdc6\f\xbd\xfb+\x88\xf4\x90K\xed\xe9\xb6=\x14\xbe\x05\xdb\x16\b\x9a\x06\x8bl2\x97\xa2\a\x8dDϰ+K\xaaH9\xdd~}!Y\xde\x19\xcf\xce$-\x8a\xf8&\x9a\"\x1f\x1f\x1f\t5m\xdb6*\xd0\x16#\x93w=\xa8@\xf8\x97\xa0\xcb'\xee\x1e~\xe0\x8e\xfcf\xba١\xa8\x9b恜\xe9\xe16\xb1\xf8\xf1\x1d\xb2OQ\xe3\x8f8\x90#!\xef\x9a\x11E\x19%\xaao\x00\x94s^T6s>\x02h\xef$zk1\xb6{t\xddC\xda\xe1.\x915\x18K\x86%\xff\xf4M\xf7m\xf7}\x03\xa0#\x96\xeb\xefiD\x165\x86\x1e\\\xb2\xb6\x01pj\xc4\x1e&oӈ\xecT\xe0\x83\x17\xeb\xf5\x9c\xac\x9b\xd0b\xf4\x1d\xf9\x86\x03\xea\x9c{\x1f}\n=\x1c\u007f\xcc!*\xae\xb9\xa6m\x89v_\xa3\xbd\xa9ъ\x83%\x96_>\xe1\xf4\x86X\x8ac\xb0)*{\x15Y\xf1ar\xfbdU\xbc\xe6\xd5\x00\x84\x88\x8cq\xc2\x0f\xee\xc1\xf9\x8f\xeegBk\xb8\x87AY\xc6\x06\x80\xb5\x0f\xd8\xc3\xdb\\AP\x1aM\x030)K\xa6ܟk\xf2\x01ݫ\xbb\xd7\xdb\xef\xee\xf5\x01G5\x1b\x01\f\xb2\x8e\x14\x8aߕb\x80\x18\x14,h\xe0\xe3\x01#¶0\a,>\"W\xe05$\xc0R\x01w\xd5\x14\xa2\x0f\x18\x85\x16\x82\xf3w\xa2\xb0'\xdb\x19\x9e\x97\x19\xf0\xec\x03&k\n\x19\xe4\x80P\x95\x81\x06\xb8\x14\x03~\x009\x10C\xc4\u0094\x93c\xab\x96\xcf\x0f\xa0\x1c\xf8\xdd\x1f\xa8\xa5\x83\xfb\xccfd\xe0\x83O\xd6d!N\x18\x05\"j\xbfw\xf4\xf7Sd\x06\xf1%\xa5U\x82\xb5\xa7\xcbGN0:e3\xd5\t\xbf\x06\xe5\f\x8c\xea\x11\"\xe6\x1c\x90\xdcI\xb4\xe2\xc2\x1d\xfc\xea#\x02\xb9\xc1\xf7p\x10\t\xdco6{\x92e\xa6\xb4\x1f\xc7\xe4H\x1e7e2h\x97\xc4G\xde\x18\x9c\xd0n\x98\xf6\xad\x8a\xfa@\x82ZRč\n\xd4\x16\xe0nV\xf9h\xbe\x8au\x00\xf9\xe5\tRy\xcc\xe2`\x89\xe4\xf6O\xe6\"\xf1\xab\xbcgm\xcfm\x9f\xaf\xcd\xf8\x8f\xf4fSf\xe5\xddO\xf7\xefaIZZ\xb0漰}\xbc\xc6G\xe23Q\xe4\x06\x8cs\xe3\x86\xe8\xc7\x12\x11\x9d\t\x9e\x9c\x94\x83\xb6\x84nM:\xa7\xddH\x92;\xfdgB\x96ܟ\x0en\xcbf\x81\x1dB\nF\t\x9a\x0e^;\xb8U#\xda[\xc5\xf8\xc5i\xcf\fs\x9b)\xfd<\xf1\xa7\vq\xed8\xb3u\x1c\xa2\xba\xaa.v\xe8\xf2\xa4\xde\aԫA\xc91h\xa0:\xb9\x83\x8f\xa0Vl\xd6)\xbe\x1c\xad;q\xbd4\xc00o\xf0\x81\xf6k\x1b\x802\xa6l\u007fe\xef\xaeܻJυZoK\x8e,\xc7\\@\x88~\"\x83\xb1]j\xab\x18R\xacE\x96\xdd\xd85\x97r\x9d1\\\v+\xe1\xce\xe1\xad\x10\xdcU\xa7\x8c!Ӻ\\\x9a\xf7\x0e\xd6\xf5W\x96\xa1\xda\xe3\xe5\xdc\xcf\xea\xcc\n\xa6\x88\xab)l\x9fB\u007fV\x1d\xa2$\xf1\u007f\xd5G\xb9T=wU#:ňNjD\xf0\xc3\n\xbe\xfa\xff\x1a\t\a\xc5\xf8I~/Ǿ\xcb\xf7\x16\xca-\r\xa8\x1f-\xce\xd1\xca6\u007f&\xa8\u007f\x8d4\u007f\xe8\xd2x\x0e\xaa\x85W\x93\"\xabv\x16\x9f\xfd\xf9\xe0ԕ\u007fW\xfa{\xa1mg\xa6\xe3\x03\xe7\xe6x*\xe4\xb5˃\xe6f~!\xe4\xa5iz\x90\x98\xe6\xe4Ui\xd5rԂ\xd2\x1a\x83\xa0y{\xfe\x96y\xf1b\xf5\x1c)G\xed\xdd<\xa6\xdc\xc3o\xbf7sT4\xdb\x05G6\xfe\x13\x00\x00\xff\xff\x9f\xfe\xa5\x85\f\n\x00\x00"), } diff --git a/pkg/generated/crds/manifests/velero.io_backups.yaml b/pkg/generated/crds/manifests/velero.io_backups.yaml index af1492426..b527f7513 100644 --- a/pkg/generated/crds/manifests/velero.io_backups.yaml +++ b/pkg/generated/crds/manifests/velero.io_backups.yaml @@ -36,9 +36,9 @@ spec: spec: description: BackupSpec defines the specification for a Velero backup. properties: - defaultRestic: - description: DefaultRestic specifies whether restic should be used to - take backup of all pod volumes by default. + defaultVolumesToRestic: + description: DefaultVolumesToRestic specifies whether restic should + be used to take a backup of all pod volumes by default. type: boolean excludedNamespaces: description: ExcludedNamespaces contains a list of namespaces that are diff --git a/pkg/generated/crds/manifests/velero.io_schedules.yaml b/pkg/generated/crds/manifests/velero.io_schedules.yaml index 67218dd8b..e612fc11d 100644 --- a/pkg/generated/crds/manifests/velero.io_schedules.yaml +++ b/pkg/generated/crds/manifests/velero.io_schedules.yaml @@ -44,9 +44,9 @@ spec: description: Template is the definition of the Backup to be run on the provided schedule properties: - defaultRestic: - description: DefaultRestic specifies whether restic should be used - to take backup of all pod volumes by default. + defaultVolumesToRestic: + description: DefaultVolumesToRestic specifies whether restic should + be used to take a backup of all pod volumes by default. type: boolean excludedNamespaces: description: ExcludedNamespaces contains a list of namespaces that diff --git a/pkg/restic/common.go b/pkg/restic/common.go index e84981e02..ffb2c1167 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -46,9 +46,9 @@ const ( // at which restic prune is run. DefaultMaintenanceFrequency = 7 * 24 * time.Hour - // DefaultRestic specifies whether restic should be used, by default, to + // DefaultVolumesToRestic specifies whether restic should be used, by default, to // take backup of all pod volumes. - DefaultRestic = false + DefaultVolumesToRestic = false // PVCNameAnnotation is the key for the annotation added to // pod volume backups when they're for a PVC. @@ -154,8 +154,8 @@ func contains(list []string, k string) bool { } // GetPodVolumesUsingRestic returns a list of volume names to backup for the provided pod. -func GetPodVolumesUsingRestic(pod *corev1api.Pod, defaultRestic bool) []string { - if !defaultRestic { +func GetPodVolumesUsingRestic(pod *corev1api.Pod, defaultVolumesToRestic bool) []string { + if !defaultVolumesToRestic { return GetVolumesToBackup(pod) } diff --git a/pkg/restic/common_test.go b/pkg/restic/common_test.go index 1e223a2e2..c69a1a44e 100644 --- a/pkg/restic/common_test.go +++ b/pkg/restic/common_test.go @@ -422,14 +422,14 @@ func TestTempCACertFile(t *testing.T) { func TestGetPodVolumesUsingRestic(t *testing.T) { testCases := []struct { - name string - pod *corev1api.Pod - expected []string - defaultRestic bool + name string + pod *corev1api.Pod + expected []string + defaultVolumesToRestic bool }{ { - name: "should get PVs from VolumesToBackupAnnotation when defaultRestic is false", - defaultRestic: false, + name: "should get PVs from VolumesToBackupAnnotation when defaultVolumesToRestic is false", + defaultVolumesToRestic: false, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -440,8 +440,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should get all pod volumes when defaultRestic is true and no PVs are excluded", - defaultRestic: true, + name: "should get all pod volumes when defaultVolumesToRestic is true and no PVs are excluded", + defaultVolumesToRestic: true, pod: &corev1api.Pod{ Spec: corev1api.PodSpec{ Volumes: []corev1api.Volume{ @@ -453,8 +453,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should get all pod volumes except ones excluded when defaultRestic is true", - defaultRestic: true, + name: "should get all pod volumes except ones excluded when defaultVolumesToRestic is true", + defaultVolumesToRestic: true, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -473,8 +473,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should exclude default service account token from restic backup", - defaultRestic: true, + name: "should exclude default service account token from restic backup", + defaultVolumesToRestic: true, pod: &corev1api.Pod{ Spec: corev1api.PodSpec{ Volumes: []corev1api.Volume{ @@ -488,8 +488,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should exclude host path volumes from restic backups", - defaultRestic: true, + name: "should exclude host path volumes from restic backups", + defaultVolumesToRestic: true, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -513,7 +513,7 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - actual := GetPodVolumesUsingRestic(tc.pod, tc.defaultRestic) + actual := GetPodVolumesUsingRestic(tc.pod, tc.defaultVolumesToRestic) sort.Strings(tc.expected) sort.Strings(actual) From 63f7690f442b03ffb93490645c01c87ab271528b Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Mon, 15 Jun 2020 18:01:00 -0700 Subject: [PATCH 07/10] more tests Signed-off-by: Ashish Amarnath --- pkg/backup/backup.go | 4 -- pkg/backup/backup_test.go | 2 +- pkg/controller/backup_controller_test.go | 76 ++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 5 deletions(-) diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index 766c8e732..a99e650e8 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -242,10 +242,6 @@ func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Req backupRequest.ResourceIncludesExcludes = getResourceIncludesExcludes(kb.discoveryHelper, backupRequest.Spec.IncludedResources, backupRequest.Spec.ExcludedResources) log.Infof("Including resources: %s", backupRequest.ResourceIncludesExcludes.IncludesString()) log.Infof("Excluding resources: %s", backupRequest.ResourceIncludesExcludes.ExcludesString()) - - if backupRequest.Backup.Spec.DefaultVolumesToRestic == nil { - backupRequest.Backup.Spec.DefaultVolumesToRestic = &kb.defaultVolumesToRestic - } log.Infof("Backing up all pod volumes using restic: %t", *backupRequest.Backup.Spec.DefaultVolumesToRestic) var err error diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index 5d9f5c8b2..df9b8c7cd 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -2752,7 +2752,7 @@ func newSnapshotLocation(ns, name, provider string) *velerov1.VolumeSnapshotLoca } func defaultBackup() *builder.BackupBuilder { - return builder.ForBackup(velerov1.DefaultNamespace, "backup-1") + return builder.ForBackup(velerov1.DefaultNamespace, "backup-1").DefaultVolumesToRestic(false) } func toUnstructuredOrFail(t *testing.T, obj interface{}) map[string]interface{} { diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 7aa3a1ef0..43e479812 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -610,6 +610,82 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, }, + { + name: "backup specifying no value for 'DefaultVolumesToRestic' gets the default true value", + backupExists: false, + backup: defaultBackup().Result(), + backupLocation: defaultBackupLocation, + // value set in the controller is different from that specified in the backup + defaultVolumesToRestic: true, + expectedResult: &velerov1api.Backup{ + TypeMeta: metav1.TypeMeta{ + Kind: "Backup", + APIVersion: "velero.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: "backup-1", + Annotations: map[string]string{ + "velero.io/source-cluster-k8s-major-version": "1", + "velero.io/source-cluster-k8s-minor-version": "16", + "velero.io/source-cluster-k8s-gitversion": "v1.16.4", + }, + Labels: map[string]string{ + "velero.io/storage-location": "loc-1", + }, + }, + Spec: velerov1api.BackupSpec{ + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToRestic: boolptr.True(), + }, + Status: velerov1api.BackupStatus{ + Phase: velerov1api.BackupPhaseCompleted, + Version: 1, + FormatVersion: "1.1.0", + StartTimestamp: ×tamp, + CompletionTimestamp: ×tamp, + Expiration: ×tamp, + }, + }, + }, + { + name: "backup specifying no value for 'DefaultVolumesToRestic' gets the default false value", + backupExists: false, + backup: defaultBackup().Result(), + backupLocation: defaultBackupLocation, + // value set in the controller is different from that specified in the backup + defaultVolumesToRestic: false, + expectedResult: &velerov1api.Backup{ + TypeMeta: metav1.TypeMeta{ + Kind: "Backup", + APIVersion: "velero.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: "backup-1", + Annotations: map[string]string{ + "velero.io/source-cluster-k8s-major-version": "1", + "velero.io/source-cluster-k8s-minor-version": "16", + "velero.io/source-cluster-k8s-gitversion": "v1.16.4", + }, + Labels: map[string]string{ + "velero.io/storage-location": "loc-1", + }, + }, + Spec: velerov1api.BackupSpec{ + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToRestic: boolptr.False(), + }, + Status: velerov1api.BackupStatus{ + Phase: velerov1api.BackupPhaseCompleted, + Version: 1, + FormatVersion: "1.1.0", + StartTimestamp: ×tamp, + CompletionTimestamp: ×tamp, + Expiration: ×tamp, + }, + }, + }, // Failed { From 6a8dca6b847744ca7a4e6592240b902e8220440e Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Tue, 16 Jun 2020 12:31:50 -0700 Subject: [PATCH 08/10] add default-volumes-to-restic flag to velero installation Signed-off-by: Ashish Amarnath --- pkg/cmd/cli/install/install.go | 8 ++++++++ pkg/install/deployment.go | 11 +++++++++++ pkg/install/resources.go | 5 +++++ 3 files changed, 24 insertions(+) diff --git a/pkg/cmd/cli/install/install.go b/pkg/cmd/cli/install/install.go index ce8185348..584d2c5b6 100644 --- a/pkg/cmd/cli/install/install.go +++ b/pkg/cmd/cli/install/install.go @@ -70,6 +70,7 @@ type InstallOptions struct { CRDsOnly bool CACertFile string Features string + DefaultVolumesToRestic bool } // BindFlags adds command line values to the options struct. @@ -103,6 +104,7 @@ func (o *InstallOptions) BindFlags(flags *pflag.FlagSet) { flags.BoolVar(&o.CRDsOnly, "crds-only", o.CRDsOnly, "only generate CustomResourceDefinition resources. Useful for updating CRDs for an existing Velero install.") flags.StringVar(&o.CACertFile, "cacert", o.CACertFile, "file containing a certificate bundle to use when verifying TLS connections to the object store. Optional.") flags.StringVar(&o.Features, "features", o.Features, "comma separated list of Velero feature flags to be set on the Velero deployment and the restic daemonset, if restic is enabled") + flags.BoolVar(&o.DefaultVolumesToRestic, "default-volumes-to-restic", o.DefaultVolumesToRestic, "bool flag to configure Velero server to use restic by default to backup all pod volumes on all backups. Optional.") } // NewInstallOptions instantiates a new, default InstallOptions struct. @@ -126,6 +128,7 @@ func NewInstallOptions() *InstallOptions { UseVolumeSnapshots: true, NoDefaultBackupLocation: false, CRDsOnly: false, + DefaultVolumesToRestic: false, } } @@ -183,6 +186,7 @@ func (o *InstallOptions) AsVeleroOptions() (*install.VeleroOptions, error) { NoDefaultBackupLocation: o.NoDefaultBackupLocation, CACertData: caCertData, Features: strings.Split(o.Features, ","), + DefaultVolumesToRestic: o.DefaultVolumesToRestic, }, nil } @@ -374,6 +378,10 @@ func (o *InstallOptions) Validate(c *cobra.Command, args []string, f client.Fact } } + if o.DefaultVolumesToRestic && !o.UseRestic { + return errors.New("--use-restic is required when using --default-volumes-to-restic") + } + switch { case o.SecretFile == "" && !o.NoSecret: return errors.New("One of --secret-file or --no-secret is required") diff --git a/pkg/install/deployment.go b/pkg/install/deployment.go index 32303cf57..f0273b745 100644 --- a/pkg/install/deployment.go +++ b/pkg/install/deployment.go @@ -40,6 +40,7 @@ type podTemplateConfig struct { defaultResticMaintenanceFrequency time.Duration plugins []string features []string + defaultVolumesToRestic bool } func WithImage(image string) podTemplateOption { @@ -107,6 +108,12 @@ func WithFeatures(features []string) podTemplateOption { } } +func WithDefaultVolumesToRestic() podTemplateOption { + return func(c *podTemplateConfig) { + c.defaultVolumesToRestic = true + } +} + func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment { // TODO: Add support for server args c := &podTemplateConfig{ @@ -129,6 +136,10 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment args = append(args, fmt.Sprintf("--features=%s", strings.Join(c.features, ","))) } + if c.defaultVolumesToRestic { + args = append(args, "--default-volumes-to-restic=true") + } + containerLabels := labels() containerLabels["deploy"] = "velero" diff --git a/pkg/install/resources.go b/pkg/install/resources.go index 35d1bc4f2..335fbce0a 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -220,6 +220,7 @@ type VeleroOptions struct { NoDefaultBackupLocation bool CACertData []byte Features []string + DefaultVolumesToRestic bool } func AllCRDs() *unstructured.UnstructuredList { @@ -287,6 +288,10 @@ func AllResources(o *VeleroOptions) (*unstructured.UnstructuredList, error) { deployOpts = append(deployOpts, WithPlugins(o.Plugins)) } + if o.DefaultVolumesToRestic { + deployOpts = append(deployOpts, WithDefaultVolumesToRestic()) + } + deploy := Deployment(o.Namespace, deployOpts...) appendUnstructured(resources, deploy) From 7abd2c6db92ee1c2fde2b21a5080641361ddff2f Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Tue, 16 Jun 2020 14:37:18 -0700 Subject: [PATCH 09/10] doc updates Signed-off-by: Ashish Amarnath --- pkg/controller/backup_controller.go | 2 +- site/docs/master/api-types/backup.md | 2 + site/docs/master/contributions/ibm-config.md | 3 +- site/docs/master/customize-installation.md | 9 ++ site/docs/master/restic.md | 97 +++++++++++++++++--- site/docs/master/velero-install.md | 1 + 6 files changed, 101 insertions(+), 13 deletions(-) diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 79d8017b1..974696cc7 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -114,6 +114,7 @@ func NewBackupController( backupTracker: backupTracker, backupLocationLister: backupLocationLister, defaultBackupLocation: defaultBackupLocation, + defaultVolumesToRestic: defaultVolumesToRestic, defaultBackupTTL: defaultBackupTTL, snapshotLocationLister: volumeSnapshotLocationLister, defaultSnapshotLocations: defaultSnapshotLocations, @@ -122,7 +123,6 @@ func NewBackupController( volumeSnapshotLister: volumeSnapshotLister, volumeSnapshotContentLister: volumeSnapshotContentLister, newBackupStore: persistence.NewObjectBackupStore, - defaultVolumesToRestic: defaultVolumesToRestic, } c.syncHandler = c.processBackup diff --git a/site/docs/master/api-types/backup.md b/site/docs/master/api-types/backup.md index 70120a921..e0addb97a 100644 --- a/site/docs/master/api-types/backup.md +++ b/site/docs/master/api-types/backup.md @@ -70,6 +70,8 @@ spec: # a default value of 30 days will be used. The default can be configured on the velero server # by passing the flag --default-backup-ttl. ttl: 24h0m0s + # Whether restic should be used to take a backup of all pod volumes by default. + defaultVolumesToRestic: true # Actions to perform at different times during a backup. The only hook currently supported is # executing a command in a container in a pod using the pod exec API. Optional. hooks: diff --git a/site/docs/master/contributions/ibm-config.md b/site/docs/master/contributions/ibm-config.md index 69d34420c..140face6d 100644 --- a/site/docs/master/contributions/ibm-config.md +++ b/site/docs/master/contributions/ibm-config.md @@ -68,7 +68,7 @@ velero install \ Velero does not currently have a volume snapshot plugin for IBM Cloud, so creating volume snapshots is disabled. -Additionally, you can specify `--use-restic` to enable restic support, and `--wait` to wait for the deployment to be ready. +Additionally, you can specify `--use-restic` to enable [restic support][16], and `--wait` to wait for the deployment to be ready. (Optional) Specify [CPU and memory resource requests and limits][15] for the Velero/restic pods. @@ -94,3 +94,4 @@ Uncomment `storageClassName: ` and replace with your `S [5]: https://console.bluemix.net/docs/containers/container_index.html#container_index [14]: http://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html [15]: customize-installation.md#customize-resource-requests-and-limits +[16]: restic.md diff --git a/site/docs/master/customize-installation.md b/site/docs/master/customize-installation.md index fcd17d774..59fca8cbc 100644 --- a/site/docs/master/customize-installation.md +++ b/site/docs/master/customize-installation.md @@ -5,6 +5,7 @@ - [Install in any namespace](#install-in-any-namespace) - [Use non-file-based identity mechanisms](#use-non-file-based-identity-mechanisms) - [Enable restic integration](#enable-restic-integration) + - [Enable default use of restic to backup pod volumes](#default-pod-volume-backup-to-restic) - [Enable features](#enable-features) - [Enable server side features](#enable-server-side-features) - [Enable client side features](#enable-client-side-features) @@ -45,6 +46,14 @@ By default, `velero install` does not install Velero's [restic integration][3]. If you've already run `velero install` without the `--use-restic` flag, you can run the same command again, including the `--use-restic` flag, to add the restic integration to your existing install. +## Default Pod Volume backup to restic + +By default, `velero install` does not enable use of restic to take backups of all pod volumes. An annotation has to be applied on every pod which contains volumes to be backed up by restic. + +To backup all pod volumes using restic without having to apply annotation on the pod, run the `velero install` command with the `--default-volumes-to-restic` flag. + +Using this flag requires restic integration to be enabled with the `--use-restic` flag. Please refer to the [restic integration][3] page for more information. + ## Enable features New features in Velero will be released as beta features behind feature flags which are not enabled by default. A full listing of Velero feature flags can be found [here][11]. diff --git a/site/docs/master/restic.md b/site/docs/master/restic.md index 762933f57..ac63ca993 100644 --- a/site/docs/master/restic.md +++ b/site/docs/master/restic.md @@ -158,7 +158,87 @@ kubectl patch storageclass/ \ You're now ready to use Velero with restic. -## Back up +## To back up + +Velero supports two approaches of discovering pod volumes that need to be backed up using restic: + +- Opt-in approach: Where every pod containing a volume to be backed up using restic must be annotated with the volume's name. +- Opt-out approach: Where all pod volumes are backed up using restic, with the ability to opt-out any volumes that should not be backed up. + +The following sections provide more details on the two approaches. + +### Using the opt-out approach + +In this approach, Velero will back up all pod volumes using restic with the exception of: + +- Volumes mounting the default service account token +- Hostpath volumes + +It is possible to exclude volumes from being backed up using the `backup.velero.io/backup-volumes-excludes` annotation on the pod. + +Instructions to back up using this approach are as follows: + +1. Run the following command on each pod that contains volumes that should **not** be backed up using restic + + ```bash + kubectl -n YOUR_POD_NAMESPACE annotate pod/YOUR_POD_NAME backup.velero.io/backup-volumes-excludes=YOUR_VOLUME_NAME_1,YOUR_VOLUME_NAME_2,... + ``` + where the volume names are the names of the volumes in the pod sepc. + + For example, in the following pod: + + ```yaml + apiVersion: v1 + kind: Pod + metadata: + name: app1 + namespace: sample + spec: + containers: + - image: k8s.gcr.io/test-webserver + name: test-webserver + volumeMounts: + - name: pvc1-vm + mountPath: /volume-1 + - name: pvc2-vm + mountPath: /volume-2 + volumes: + - name: pvc1-vm + persistentVolumeClaim: + claimName: pvc1 + - name: pvc2-vm + claimName: pvc2 + ``` + to exclude restic backup of volume `pvc1-vm`, you would run: + + ```bash + kubectl -n sample annotate pod/app1 backup.velero.io/backup-volumes-excludes=pvc1-vm + ``` + +2. Take a Velero backup: + + ```bash + velero backup create BACKUP_NAME --default-volumes-to-restic OTHER_OPTIONS + ``` + + The above steps uses the opt-out approach on a per backup basis. + + Alternatively, this behavior may be enabled on all velero backups running the `velero install` command with the `--default-volumes-to-restic` flag. Refer [install overview][11] for details. + +3. When the backup completes, view information about the backups: + + ```bash + velero backup describe YOUR_BACKUP_NAME + ``` + ```bash + kubectl -n velero get podvolumebackups -l velero.io/backup-name=YOUR_BACKUP_NAME -o yaml + ``` + +### Using opt-in pod volume backup + +Velero, by default, uses this approach to discover pod volumes that need to be backed up using restic, where every pod containing a volume to be backed up using restic must be annotated with the volume's name. + +Instructions to back up using this approach are as follows: 1. Run the following for each pod that contains a volume to back up: @@ -216,7 +296,9 @@ You're now ready to use Velero with restic. kubectl -n velero get podvolumebackups -l velero.io/backup-name=YOUR_BACKUP_NAME -o yaml ``` -## Restore +## To restore + +Regardless of how volumes are discovered for backup using restic, the process of restoring remains the same. 1. Restore from your Velero backup: @@ -370,8 +452,7 @@ on that node. The controller executes `restic restore` commands to restore pod v ### Backup -1. The main Velero backup process checks each pod that it's backing up for the annotation specifying a restic backup -should be taken (`backup.velero.io/backup-volumes`) +1. Based on configuration, the main Velero backup process uses the opt-in or opt-out approach to check each pod that it's backing up for the volumes to be backed up using restic. 1. When found, Velero first ensures a restic repository exists for the pod's namespace, by: - checking if a `ResticRepository` custom resource already exists - if not, creating a new one, and waiting for the `ResticRepository` controller to init/check it @@ -418,12 +499,6 @@ Velero does not currently provide a mechanism to detect persistent volume claims To solve this, a controller was written by Thomann Bits&Beats: [velero-pvc-watcher][7] -### Add backup annotation - -Velero does not currently provide a single command or automatic way to backup all volume resources in the cluster without annotating pods or pod templates. - -The [velero-volume-controller][10] written by duyanghao helps to solve this problem by adding backup annotation to pods with volumes automatically. - [1]: https://github.com/restic/restic [2]: customize-installation.md#enable-restic-integration [3]: https://github.com/vmware-tanzu/velero/releases/ @@ -433,4 +508,4 @@ The [velero-volume-controller][10] written by duyanghao helps to solve this prob [7]: https://github.com/bitsbeats/velero-pvc-watcher [8]: https://docs.microsoft.com/en-us/azure/aks/azure-files-dynamic-pv [9]: https://github.com/restic/restic/issues/1800 -[10]: https://github.com/duyanghao/velero-volume-controller \ No newline at end of file +[11]: customize-installation.md#default-pod-volume-backup-to-restic diff --git a/site/docs/master/velero-install.md b/site/docs/master/velero-install.md index 58718d16c..d0793bf0c 100644 --- a/site/docs/master/velero-install.md +++ b/site/docs/master/velero-install.md @@ -19,6 +19,7 @@ velero install \ --velero-pod-cpu-limit \ --velero-pod-mem-limit \ [--use-restic] \ + [--default-volumes-to-restic] \ [--restic-pod-cpu-request ] \ [--restic-pod-mem-request ] \ [--restic-pod-cpu-limit ] \ From e5e7c025dd04fc1cc715e7acfea49bab2cd10c53 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Thu, 18 Jun 2020 14:14:15 -0700 Subject: [PATCH 10/10] fix copyright boilerplate Signed-off-by: Ashish Amarnath --- pkg/apis/velero/v1/backup.go | 2 +- pkg/backup/backup.go | 2 +- pkg/backup/backup_test.go | 2 +- pkg/backup/item_backupper.go | 2 +- pkg/builder/backup_builder.go | 2 +- pkg/cmd/cli/backup/create.go | 2 +- pkg/cmd/cli/install/install.go | 2 +- pkg/cmd/server/server.go | 2 +- pkg/controller/backup_controller.go | 2 +- pkg/controller/backup_controller_test.go | 2 +- pkg/install/deployment.go | 2 +- pkg/install/resources.go | 2 +- pkg/restic/command.go | 2 +- pkg/restic/command_test.go | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pkg/apis/velero/v1/backup.go b/pkg/apis/velero/v1/backup.go index 8d50672c2..d1e2cb48c 100644 --- a/pkg/apis/velero/v1/backup.go +++ b/pkg/apis/velero/v1/backup.go @@ -1,5 +1,5 @@ /* -Copyright 2017, 2019 the Velero contributors. +Copyright 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index a99e650e8..026aba6b7 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -1,5 +1,5 @@ /* -Copyright 2017, 2020 the Velero contributors. +Copyright 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index df9b8c7cd..d4bdc9aa5 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -1,5 +1,5 @@ /* -Copyright 2019 the Velero contributors. +Copyright 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index c85a6dc7a..d9fd2822b 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -1,5 +1,5 @@ /* -Copyright 2017 the Velero contributors. +Copyright 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/builder/backup_builder.go b/pkg/builder/backup_builder.go index 4bcd53fc6..75f95d950 100644 --- a/pkg/builder/backup_builder.go +++ b/pkg/builder/backup_builder.go @@ -1,5 +1,5 @@ /* -Copyright 2019 the Velero contributors. +Copyright 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index 5061f436d..d73928c0d 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -1,5 +1,5 @@ /* -Copyright 2017 the Velero contributors. +Copyright 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/cmd/cli/install/install.go b/pkg/cmd/cli/install/install.go index 584d2c5b6..1065a1729 100644 --- a/pkg/cmd/cli/install/install.go +++ b/pkg/cmd/cli/install/install.go @@ -1,5 +1,5 @@ /* -Copyright 2019,2020 the Velero contributors. +Copyright 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 669b03e6e..48c032b09 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -1,5 +1,5 @@ /* -Copyright 2017, 2019, 2020 the Velero contributors. +Copyright 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 974696cc7..85380144b 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -1,5 +1,5 @@ /* -Copyright 2017, 2019 the Velero contributors. +Copyright 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 43e479812..b746cb734 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -1,5 +1,5 @@ /* -Copyright 2017, 2019 the Velero contributors. +Copyright 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/install/deployment.go b/pkg/install/deployment.go index f0273b745..758736a75 100644 --- a/pkg/install/deployment.go +++ b/pkg/install/deployment.go @@ -1,5 +1,5 @@ /* -Copyright 2018, 2019, 2020 the Velero contributors. +Copyright 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/install/resources.go b/pkg/install/resources.go index 335fbce0a..b88dbb183 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -1,5 +1,5 @@ /* -Copyright 2018, 2019, 2020 the Velero contributors. +Copyright 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/restic/command.go b/pkg/restic/command.go index ab6f0aedc..d06968018 100644 --- a/pkg/restic/command.go +++ b/pkg/restic/command.go @@ -1,5 +1,5 @@ /* -Copyright 2018 the Velero contributors. +Copyright 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/restic/command_test.go b/pkg/restic/command_test.go index 349796976..dc8ff0f29 100644 --- a/pkg/restic/command_test.go +++ b/pkg/restic/command_test.go @@ -1,5 +1,5 @@ /* -Copyright 2018 the Velero contributors. +Copyright 2020 the Velero contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.