diff --git a/changelogs/unreleased/5370-lyndon b/changelogs/unreleased/5370-lyndon new file mode 100644 index 0000000000..11351c3174 --- /dev/null +++ b/changelogs/unreleased/5370-lyndon @@ -0,0 +1 @@ +Pod Volume Backup/Restore Refactor: Rename parameters in CRDs and commands to remove "Restic" word \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_backups.yaml b/config/crd/v1/bases/velero.io_backups.yaml index eebfedf705..e204e1157b 100644 --- a/config/crd/v1/bases/velero.io_backups.yaml +++ b/config/crd/v1/bases/velero.io_backups.yaml @@ -42,9 +42,17 @@ spec: CSI VolumeSnapshot status turns to ReadyToUse during creation, before returning error as timeout. The default value is 10 minute. type: string + defaultVolumesToFsBackup: + description: DefaultVolumesToFsBackup specifies whether pod volume + file system backup should be used for all volumes by default. + nullable: true + type: boolean defaultVolumesToRestic: - description: DefaultVolumesToRestic specifies whether restic should - be used to take a backup of all pod volumes by default. + description: "DefaultVolumesToRestic specifies whether restic should + be used to take a backup of all pod volumes by default. \n Deprecated: + this field is no longer used and will be removed entirely in future. + Use DefaultVolumesToFsBackup instead." + nullable: true type: boolean excludedNamespaces: description: ExcludedNamespaces contains a list of namespaces that diff --git a/config/crd/v1/bases/velero.io_schedules.yaml b/config/crd/v1/bases/velero.io_schedules.yaml index 246bafb3f7..cd2010c504 100644 --- a/config/crd/v1/bases/velero.io_schedules.yaml +++ b/config/crd/v1/bases/velero.io_schedules.yaml @@ -72,9 +72,17 @@ spec: for CSI VolumeSnapshot status turns to ReadyToUse during creation, before returning error as timeout. The default value is 10 minute. type: string + defaultVolumesToFsBackup: + description: DefaultVolumesToFsBackup specifies whether pod volume + file system backup should be used for all volumes by default. + nullable: true + type: boolean defaultVolumesToRestic: - description: DefaultVolumesToRestic specifies whether restic should - be used to take a backup of all pod volumes by default. + description: "DefaultVolumesToRestic specifies whether restic + should be used to take a backup of all pod volumes by default. + \n Deprecated: this field is no longer used and will be removed + entirely in future. Use DefaultVolumesToFsBackup instead." + nullable: true type: boolean excludedNamespaces: description: ExcludedNamespaces contains a list of namespaces diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 14cd3c51ba..44ff941276 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -30,14 +30,14 @@ import ( var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WAo\xdc6\x13\xbd\xebW\f\xf2\x1dr\xf9\xa4M\xd0C\v\xddR\xb7\x05\x82&\x86a\a\xbe\x14=P\xe4\xec.c\x8ad\xc9\xe1\xa6ۢ\xff\xbd\x18R\xf2j%\xd9\x1b\a\xa8n\"\x87of\xde\xcc\x1bQU]ו\xf0\xfa\x1eC\xd4ζ \xbc\xc6?\t-\xbf\xc5\xe6\xe1\x87\xd8h\xb79\xbc\xad\x1e\xb4U-\\\xa5H\xae\xbf\xc5\xe8R\x90\xf8\x13n\xb5դ\x9d\xadz$\xa1\x04\x89\xb6\x02\x10\xd6:\x12\xbc\x1c\xf9\x15@:K\xc1\x19\x83\xa1ޡm\x1eR\x87]\xd2Fa\xc8\xe0\xa3\xebÛ\xe6\xfb\xe6M\x05 \x03\xe6\xe3\x9ft\x8f\x91D\xef[\xb0ɘ\n\xc0\x8a\x1e[\xe8\x84|H>\xa0wQ\x93\v\x1acs@\x83\xc15\xdaUѣd\xb7\xbb\xe0\x92o\xe1\xb4QN\x0f!\x95t~\xcc@\xb7#\xd01o\x19\x1d\xe9\xd7\xd5\xed\x0f:R6\xf1&\x05a\xd6\x02\xc9\xdbQ\xdb]2\",\f\xd8A\x94\xcec\v\xd7\x1c\x8b\x17\x12U\x050P\x90c\xabA(\x95I\x15\xe6&hK\x18\xae\x9cI\xfdHf\r\x9f\xa3\xb37\x82\xf6-4#\xed͂\xb2l;\x12\xf6n\x87\xc3;\x1dٹ\x12\x84K0f\xae9\xc5\xfa\xe9\xe8\xf1\f\xe5D\x04L\xf6\nb\xa4\xa0\xed\xae:\x19\x1f\xde\x16*\xe4\x1e{\xd1\x0e\xb6Σ}w\xf3\xfe\xfe\xbb\xbb\xb3e\x00\x1f\x9c\xc7@z,Oy&}9Y\x05P\x18eОr\u05fcf\xc0b\x05\x8a\x1b\x12#\xd0\x1eGNQ\r1\x80\xdb\x02\xedu\x84\x80>`D[Z\xf4\f\x18\xd8HXp\xddg\x94\xd4\xc0\x1d\x06\x86\x81\xb8w\xc9(\xee\xe3\x03\x06\x82\x80\xd2\xed\xac\xfe\xeb\x11;\x02\xb9\xec\xd4\b¡GNO\xae\xa1\x15\x06\x0e\xc2$\xfc?\b\xab\xa0\x17G\b\xc8^ \xd9\t^6\x89\r|t\x01AۭkaO\xe4c\xbb\xd9\xec4\x8dz\x94\xae\xef\x93\xd5t\xdcdi\xe9.\x91\vq\xa3\xf0\x80f\x13\xf5\xae\x16A\xee5\xa1\xa4\x14p#\xbc\xaes\xe86k\xb2\xe9\xd5\xff\u00a0\xe0\xf8\xfa,\xd6E-˓\xc5\xf2L\x05X-\xa0#\x88\xe1h\xc9\xe2D4/1;\xb7?\xdf}\x82\xd1u.Ɯ\xfd\xcc\xfb\xe9`<\x95\x80\t\xd3v\x8b\xa1\x14q\x1b\\\x9f1\xd1*ﴥ\xfc\"\x8dF;\xa7?\xa6\xae\xd7\xc4u\xff#a$\xaeU\x03WyHA\x87\x90<\xabA5\xf0\xde\u0095\xe8\xd1\\\x89\x88\xffy\x01\x98\xe9X3\xb1_W\x82\xe9|\x9d\x1b\x17\xd6&\x1b\xe3\b|\xa2^\xf3\xb1v\xe7Qr\xf9\x98A>\xaa\xb7Zfm\xc0\xd6\x05\x10\v\xfb\xe6\fz]\xba\xfc\x94\xe1wG.\x88\x1d~p\x05sn\xb4\x1a\xdb\xec\xcc\x18\x1cO\x96\"c\\7\\`\x03\xd0^\xd0D\xbf$\xb4}\x1c\x03\xab\xf90\x92-S\bhi\x80\xc97\x88o\x1e\x99FD\x9a\x8c\v\xbe\xcd]\xe8\x80\x0f\xcb\x13c`\f\x06\xc4\v\xd3\xf9\xf2E̿\xba\xb9hk\x93e\xebB/\xa8\\\x17k\x06ZX\xf0\xb5\\t\x06[\xa0\x90\x96\xdb\xcf\xcdQ\x8cQ\xec.e\xf7\xb1X\x95\xcb\xc5p\x04D\xe7\x12=A=\xed\x97Q\xc0\x85r\\\x88\xd4\xefE\xbc\x14\xe7\r۬5\xc4\xec{\xf5\\\bO\xcd\xcck\xfc\xb2\xb2z\x8bB-u\\õ\xa3\xf5\xad'3\\U\xc5b1\xf2=LM\xea\x1c\x8b\x90\xa7+\xa9{\xbcW\xb6\xf0\xf7?\xd5IXBJ\xf4\x84\xeaz\xfe\a6\xcc\xf7\xf1\x87*\xbfJg\xcb\x0fPl\xe1\xb7߫\xe2\n\xd5\xfd\xf8\x93ċ\xff\x06\x00\x00\xff\xff\xc8p\x98۸\x0e\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec=Msܸrw\xfd\x8a.\xe5\xe0\xf7\xaa4\xa3u吔n^\xd9[Q\xedƫ\xb2\xf4\x9cC*\a\f\xd93\x83\x15\t\xf0\x01\xe0ȓT\xfe{\xaa\x1b\xe0\xe7\x80$F+\xbd\xb7/e\\\xec!\x81F\xa3\xd1\xe8/4[\x17\xab\xd5\xeaBT\xf2+\x1a+\xb5\xba\x01QI\xfc\xe6P\xd1/\xbb~\xfaW\xbb\x96\xfa\xfa\xf0\xfe\xe2I\xaa\xfc\x06nk\xebt\xf9\x05\xad\xaeM\x86\x1fq+\x95tR\xab\x8b\x12\x9dȅ\x137\x17\x00B)\xed\x04=\xb6\xf4\x13 \xd3\xca\x19]\x14hV;T\xeb\xa7z\x83\x9bZ\x169\x1a\x06\xdeL}\xf8a\xfd/\xeb\x1f.\x002\x83<\xfcQ\x96h\x9d(\xab\x1bPuQ\\\x00(Q\xe2\rlD\xf6TWv}\xc0\x02\x8d^K}a+\xcch\xae\x9d\xd1uu\x03\xdd\v?$\xe0\xe1\xd7\xf0#\x8f\xe6\a\x85\xb4\xee\xe7\xde\xc3_\xa4u\xfc\xa2*j#\x8av&~f\xa5\xdaՅ0\xcd\xd3\v\x00\x9b\xe9\no\xe03MQ\x89\f\xf3\v\x80\xb0\x1c\x9er\x15\x10>\xbc\xf7\x10\xb2=\x96\xc2\xe3\x02\xa0+T\x1f\xee\xef\xbe\xfe\xf3\xc3\xe01@\x8e63\xb2rL\x14\x8f\x18H\v\x02\xbe\xf2\xb2\xc0\x04\xf2\x83\xdb\v\a\x06+\x83\x16\x95\xb3\xe0\xf6\b\x99\xa8\\m\x10\xf4\x16~\xae7h\x14:\xb4-h\x80\xac\xa8\xadC\x03\xd6\t\x87 \x1c\b\xa8\xb4T\x0e\xa4\x02'K\x84?}\xb8\xbf\x03\xbd\xf9\r3gA\xa8\x1c\x84\xb5:\x93\xc2a\x0e\a]\xd4%\xfa\xb1\u007f^\xb7P+\xa3+4N6t\xf6\xad\xc7U\xbd\xa7\xa3\xe5\xbd#\n\xf8^\x90\x13;\xa1_F\xa0\"\xe6\x81h\xb4\x1e\xb7\x97\xb6[.s\xc8\x000P'\xa1\x02\xf2kx@C`\xc0\xeeu]\xe4ą\a4D\xb0L\xef\x94\xfc\xef\x16\xb6\x05\xa7y\xd2B8\f\f\xd05\xa9\x1c\x1a%\n8\x88\xa2\xc6+&I)\x8e`\x90f\x81Z\xf5\xe0q\x17\xbb\x86\u007f\xd7\x06A\xaa\xad\xbe\x81\xbds\x95\xbd\xb9\xbe\xdeIל\xa6L\x97e\xad\xa4;^\xf3\xc1\x90\x9b\xdaic\xafs<`qm\xe5n%L\xb6\x97\x0e3\xda\xc8kQ\xc9\x15\xa3\xae\xf8D\xad\xcb\xfc\x9f\x1a\x06\xb0\xef\x06\xb8\xba#1\xa3uF\xaa]\xef\x05s\xfd\xcc\x0e\xd0\x01\xf0\xfc\xe5\x87\xfaUt\x84\xa6GD\x9d/\x9f\x1e\x1e\xfb\xbc'\xed\x98\xfaL\xf7\x1eCv[@\x04\x93j\x8b\xc6o\xe2\xd6\xe8\x92a\xa2\xca=\xf71\xeb\x16\x12\u0558\xfc\xb6ޔ\xd2Ѿ\xff\xb5FKL\xae\xd7p\xcb\"\x066\bu\x95\x13g\xae\xe1N\xc1\xad(\xb1\xb8\x15\x16\xdf|\x03\x88\xd2vE\x84Mۂ\xbet\x1cw\xf6T\xeb\xbdhd\xd9\xc4~y\x81\xf0Pa6804Jne\xc6\xc7\x02\xb6\xdat\xf2\u008b\xab\xf5\x00d\xfc\xc8Rˬ|P\xa2\xb2{\xedH\xfe\xeaڍ{\x8c\x10\xba}\xb8\x1b\rh\x90\t\xa8\xb1X\xa9-\xe6tΞ\x85t\x84\xde\tL @\xf0\x95%L\x03\x8f%Mm\xc1\xd5F\xf1)\xfd\x82\"?>\xea\xbfX\x84\xbcffmt\xc5\x15lp\xab\rF\xe0\x1a\xa4\xf1\xd4\x19\x8d!\xc2XFI\xd7n\r\x8f{$2\x8a\xbap\x81聾\xf7?@)U\xedp}\x02mb\x83=Q\x18\x8c_\x81}\xd4_\xd0:\x99-\x10\xefctP\x8f\x80\xcf{t{4t\xf0\xf8\x05˲\xc8\"7\x1d\x89\x9dxB\x10a\xdbY&\x16\x05T\xba\x11\xdf\x166\xc7\x06٩\x05n\xb4.P\x8c\xc5+~ˊ:Ǽ\xd5w'\xcc3Zݧ\x93\x01l\v\b\xa9Hܐ\xf6%\xf4T\xf7\x964Zdq\xc2 Ё\x97\xca\xc3ce\xb5\xc7(gS\x93\x0e\xcb\bn\xb3\xdb\alc\x88M\x817\xe0L}\xcaH~\xac0F\x1c'\xe8\xd2\xd8E\xa9di\xfb\a\xf1[Ȍ\x15w+d\x992^͋(k\xff\x81\x89\xb2\xd7\xfai\x89\x10\xffF}:\x85\x01\x19\x9b\x97\xb0\xc1\xbd8Hm\xc2҃\xfe\xde \xe07\xccj\x871\xfe\x17\x0er\xb9ݢ!8\xd5^X\xb4\xdef\x98&ȴ\f\x04\x96\x1a\x93\x9by\xb2\x8en#\x89Sy\xe5S\xa8Ӂ\x1e\x9f\xab\xa6\x11\xa2$\xa6\xc8\xdeS\xb9<ȼ\x16\x05He\x9dP\x99_\x8fh\xf1:]\x0f\xccm\xf2\t\xce^\x8f4\x98\xd3N\ft\x8aV\b\xda@I\x8a\xf4\xb4\xebX\xf5wmj\xd9\x1bA\xd2I{\x165u\x816L\x95\xb3\xb2\xead\xc0\xd5$\xe8vG\xbc\x11V\x88\r\x16`\xb1\xc0\xcci\x13'\xc7\xd2&\xfb\x96\"\xd7&\xa8\x18\x91pC\xe5\xd7-l\x06$\xb0f\xdc\xcbl\xef\xed#\xe2 \x86\x03\xb9F˧\\TUq\x9cZ$,\xed|\x98d\xee\xa0wm\xe1ȏ\xe1\xc5\x0e\u007f\xd7\x12dc\xd7\x16\xa4䐲-;\x80ӳ\xcb\xfe\xffI\xd8F쿀i\xefN\x86\xbe.\xd3\x12I%\xf9Aw[\xc0\xb2r\xc7+\x90\xaey\xba\x04\x91\x8c\x95n\xfe\u007f\xe0\x8d9\x9f\xe3\xef\xc6#_\x95\xe3gwe\t\"\xedJ;\xfd?র\xb2x\b\xba\"yC~鏺\x02\xb9m7$\xbf\x82\xad,\x1c\x9a\xd1\xce\xfc\xae\xf3\xf2\x1a\xc4H\xd1w\xd4J\xe1\xb2\xfd\xa7ody\xd9.R\x97H\x97\xf1`o\xbf6\xf6\xfcP1/\xc0\x05\xf6\xec\xa5\xc1\xd2G\f\x1e\x99\x9a\xdd\x13\xb6\xa8>|\xfe\x88\xf9\x1cy \x8d\xf3N\x16\xf2a\x84l\u007f\xea`\x94\xa7.#\x98>\xad\u007f\xe3cAW \xe0\t\x8f\xdeb\x11\nhs\x04M4\xe1\xe9\x9c\x12\x87\x83R\xccdOxd0!ʴ8:\x95\x15|{\xc2cJ\xb7\x11\x01\t'iC\xf4\x8c(I\x0f\x98\x10\x1c\x94H'\x1epİ\x91Eˋ\x83tAҴ\x86\xf6/Xf\xbbm\xbdh+o\xec;뷈N\xc1^V\x89\v%5\a\x16\xf9\xb441ï\xa2\x90y;\x91\xe7\xfb;5m\r\x0f\xdbg\xed\xee\xd4\x15|\xfa&m\b\xdb~\xd4h?k\xc7Oބ\x9c\x1e\xf1\x17\x10\xd3\x0f\xe4㥼\xd8&:\xf4\x83\x8f\t\xcc\xed\u06dd\xf7\xf0\xda\xed\x91\x16\xee\x14\xf9-\x81\x1e\x1cJ\xf6\xd3\xcd\xeb\x87a+k\xcb\xd1E\xa5ՊU\xe5:6\x93'v\"Hm\x06;r\x8aZ;\xa9\x9f0\x11\xec#i\x12?\xde\a\xc7\v\x91a\xde\x04\xc78\xa4+\x1c\xeed\x06%\x9aݜ\xe2跊\xe4{\x1a\n\x89R\u05f739,M\xb57-\x88\xee|\x19\x99\x15\x9d܄^\xcdf/v\x9d\x88\xe4Nw]^\x11\xabX\xb6?\x16\xa9+\xf2\x9c/\xe1Dq\u007f\x86\xc4?c/Nu\xbfG\xcck\xc8Rp\x90\xf1\u007fH\xcd1C\xff/TB\x9a\x843\xfc\x81\xef\xd4\n\x1c\x8c\rQ\xac\xfe44\x83\xb4@\xfb{\x10\xc5\xe9\x1dAdq\x9ad\v\x16^\x91\xeb\xed\x89\xc5r\x05\xcf{m\xbdN\xddJ\x8c\x86T\x87MZ\xb8|\xc2\xe3\xe5Չ\x1c\xb8\xbcS\x97^\xc1\x9f-nZkA\xab\xe2\b\x97<\xf6\xf2\xf7\x18A\x89\x9c\x98ԍ\xef.SMe\xf2%\x1bK\x80\x06\xb6\x17vd\xe6\xcea\x9dć\x95\xb6\x91k\x88\tT\xee\xb5u>\xb280Kωb\x81\xe7\xa1\x10\xbd\x02\xb1\xf5W\xa6\xda4\x97a$\xf6F\x01W\xda5;/ai\x1bۈ\x98\aJ\x8e\xd5ew\x82\xbd<\xbd\xf47d<\x89\xc8ظX\x84[\x19\x9d\xa1\xb5\xf3,\x92 \xad\x17\x82\x84m\x80Px\a\xc6\xdf4\xcd\a%\x9b\x96n\x90\x12\x91\xce4\xe5?}\xebE/\xe9\xf0\xd3\xef%\xe6;\x17/\xe03[\x96b|\xa5\x9a\x84\xe2\xad\x1f\xd9\x1c\x93\x00Ȼ\x06fW\xf3QO\xb7 \x03#\xfd\x11\xd4t)\xd5\x1dO\x00\xef_]\xad\xb7B\x12_b\xb8\xdf6c;\xa2\xb7\x0f\xf8\xf4\xa6ZD\x9a#\xf7\x06\a;w\x1a\xe7&C1\x11\xa4Ү\x1fN \xb8\x95\xce\xdfY\xd8Jc]\x1f\xd1T\xa6\xa8\x17N\u007f\xd7\xce\xf5\x9c\xd4'c^\xe48\xfd\xeaG\xf6\x02Y{\xfd\xdc\\LO^f\xc6\x1a_\n!\xc8-H\a\xa82]+\x0e\xbf\xd0Q\xe7)\xfc\x16x\x01\x9dL\xb24\x01A\rU]\xa6\x11`\xc5\\'\xd5l\x9c\xa6\xdf\xfd'!\x8b\xb7\xd867u\u007f\x1fk\x83mk.\xf2\xfb\x19\x06\xa5\xf8&˺\x04Q\x12\xe9Sݞ\xad\xbf\xfe\x1f\xecx\x9b\x04\xc0pY\x8d8M\x87\xaa*Х\x9eH\u007f\xddO\xc7\xc4\xca\x1c[\xc5\x1c\xb8@+\x10\xb0\x15\xb2\xa8M\xa2\x84<\x8b\xb6\xe7\xf8\x1aAX\xbc\x9e\x13\x916\xf9\x8aI\x91\x10\x88M4\x16\xe7\xa5ue\xd2M\xc5{\x83i\xe6\xd9RP\xba1\xcf*#\x89\x97\xf4k[h\x81ń:~7\xd1N\xdaw\x13m\xa1}7\xd1&\xdbw\x13m\xb9}7\xd1B\xfbn\xa25\xed\xbb\x89\xf6\xddD\x9b\xeb6'\xad\x970\xf2\x9f*L\xbc\\\xc4\"\xe1zz\x0e\xc5\x19\xf8!\x9b\xe2\xd6\u007f\xb6\x90\x9aay\x17\x1f\x15ɫ\r\xdfC\xac\xf8S\x8e\x18\atI\x17\x9d*iS.\xe9\x804\xec\xed3\xaf\x17\x920\x93\xd2)\xe3ٷ)\t?Ki>\xc3<\xd36ͦI4\xd5\xcd$\x11:4\x9f\x84\x90\xd9\xdb\xcf!\x19\xe6밝\xdb`\xfaw\xcfAMH\xc5YH\xc0\x99O̝\xa3\xd7\xc8\xf5\x18\x12\xcc\f\x12F\xff0\xf4ZȒ\x99\u038d\t7A\xe8\xc4\xe1\xfdz\xf8\xc6\xe9\x90)\x03\xcf\xd2\xed#Kyޣ\xe2;,\xb5맽6\xfc\x16\xbe\xcd\x19\xd3\x11\xb4\x01%\v&\xe7\f\xb7\x0e\xc8\v\xbfVޅ;\xfb\\λ\x1fi\xb94/Π\x19f\xc8L\x88\xe8s\xaf\x8c\xd2\x13\x85\xd3sd\xe6\x93Z\xceɌ\x19\xe7\xbdL\x02]·I\xf1\x1c\x17r_^\x90\xf1\x92\x98\xed\xf8\xbb/\xc6RrZ^\x94ɲ\x98\x10\x98\x98\xbf2\xccL\x99\ayF\xd6J\x12q\x963T\xce\xceK\ty \xb3\xebH\xceF\x89\xe4\x99\xcc\x02\x9e\xccA\x99\xcb.Y\x88J\x9df\x9e\xa4\xe7\x94̂\xe6|\x93\xe5L\x92\xd7\xcb\x17}\r\x1bxZ\xd4,f\x83,\xda\xc8\xf3\xf8-\xe6{\x9c\x93\xe5\xb1H\xb1\x17ft\xb4\x19\x1b\x13\xf3\x9e\x9b\xc71\xccӘ\x00\x9a\x92\xbd1\x91\x9d1\x01q6g#5'c\x02\xf6\x82ڝ咙\x97\xf1/HaQ\xbf\x15\u007f+\x8ez\xe9´\x19\x98\x8bK\x16\xfa\xaf\xa3\ued17\x8d\xd54o~\xc6,O\xe9\xf6盟e]8Y\x15\x1c\xce?\xc8<\xea4\xba=\x1e\xe1Y\x16\x05\x89\xd5\xdf4\u007f\xe6\xb492\xa4_\xbf\xb4\xec\xb9\x1e\x19\xd1\xc2\xc23\x16\x05\x88\x18s\x9d\xac<\xf3\x1fAgz\x85$\xf3\xe9\xc0\x85O>÷\xd2W\x9e\x83\xf9K\xaeX\xc4\xd3\xed\xb1$(ͷ\xa3g\xb8\x1f\xf3\x06\xa2\xb7e\xf9\xd9_k4G\xd0\a4\x9dŰ\xf0\x1d\x81?h\xb6.\xbaĭ ?\xfc\xa7\xf7#ù;p\xf0Ay\x15\x16\x05;\u0091\xe1Й/ڽ&\xf1F~\xc0D\xd7x\xe0C\xb7\xa3#\xef\x97l\xcf\xd4$\xfc\xb7u\x1d\xcew\x1e\x16\xd5\xf6\x9b8\x10/w!f@\xa6&է]@-&ѿ\x95+\xb1\xe4L$[QiI\xf2o\x91\x1c\u007fFR\xfc\x19N\xc5ynE2\x99R\x92\xdf\xdfĹxC\xf7\xe2-\x1c\x8c\x97\xb9\x18\v GI\xed)\xe9\xeaI\x97\xab\xc9\xf7\v)\x97\xa3\xcbW\x00\xf3i\xe8\t\xe9\xe7\t\x97\x03K\x98&\xa4\x99\x9f\x97^\x9e@\xc37r>\xde\xc8\xfdx\v\a\xe4m]\x90E'd\x91sf_\xbf8\xba\xacM\x8ef6\x18\x9f\xcaj\xb3L6\xf2\x17\x86s\x8e\xbe\xa8mj\xa4P\xaf\x81i\x1a\v)\xb7_\u007ff\xf0\xb3T\xb9\xdf\x0fb\xaa\x9e\x1e\xe7bJ\x9c\xff\xde\x1a\x15\x9d}\x16\a:\xbaT\xb0X\t\xc3ն6G\u007f1i\xd7\xf0Id\xfbaG\xd8\v\v[mʨ\xc1t\xd9\xde\xc8\\7\xa3\xe8\xc9\xe5\x1a\xe0'\xdd^z\xf5+*XYVő\xfc\x00\xb8\x1c\x0ey\x19\x03D\x99dž\xba>\xa1\xdc͂\xaf\xf70\xec\x1d\xb9\xbck\x8a\xddd\x85\xae\xf3\x16\xfa\xc4\xe6\tu\x84\xfb\xafl\x93p\x99\x90\xac+\x99\x12\xac\x8e\xc6\xe7\x1bWT\xf9\xf1\xf5/\xf3\xac\xd3F\xec\xf0\x17\xed\v6-Qb\xd8{P\xad+Ȋ\xe6r\xbd\xf9\xf6\"\xa6CC\xe9\xa8\x11\xb0.g&\x9c\x86\ue793\xb0\x8c\t\x91\x99\xf3\xe7\\\xb1\xb0\x98\xc7\xc7_\xfc\x02\x9c,q\xfd\xb1\xf6\x17\xa7\xabJ\x18\x8bD\xcdfa~І\xfe\xbb\xd7ϱ؆\x0ek\xfeq\x8c\xb7A\xce\xcb\xe1\xfbٳ\xb0?\f\xcaO5$Zbԯ\xf1Q=Ǭ\xb7I\xfe\x94G\x1d\xf2)8\xbd\n|\x1c\xb2\xe0\xefj^\xb7\xccϔԞ\xaaQ\xc6u\xb9\x96\xab\x94\xf9\xf2]\xa1&a\xc8\xee\xaa\r\xd7\xe8\t\xa5\xbd\xb8\xa6\xcd\xcb\n\x95\xf9d\x94A\x9d\xc8\xf9}\xba=\x1d\xc1\xd5\x00M\xde+T\xd6\x16\xcez\x16\xb6Mx\x89*\xd2\x0e\x9c\x1fɖ,A\xc3\x1c\xf0\x80\n\xb4\xe2\xfc\x16\xae~\xe3+V\x8e\xc7D\xa0\xf6\xa1\x84\x04\x9a\xba*\xb4ț\x13\xde\xe8\xacP\xe5\xf0\x91\xe5\x979\xa0ygg`rq\xb0\xad61\"\x9c\nL\xafXn \x17\x0eWQ\xa0I\xb2/\xcal\x99\x95CF\xb7\x1f\x9c#\xbf f+\x8f+\xcdM\x8dl\xf4\xaf\xd3N\x14\xa0\xear\xe3\x15\xbah:\xc4\xf6\xef\xa4ޜ\r\x19O3\xc7\xcb/L*\x87\xbb\x93\x98\xe2\xe9\xcan\x1b\xfe9{e\xedȩ\x95\xd9:\xcb\xd0\xdam]\x141Ӿ\xe5\xdc\xd7_&\xe7\xf2-\xd68\xe3N^\x04r\"`S\x88\xceg\x02\x96h\xad\xd85\xc5͞I\x03\xedP!\x1b>\xb1x\xa3w\f\xbḇai/\x1f\xc1\x12\x99\xabE\x98\xa0\xb9\xf9\xef\xf5z\x17\xb3\v\n\xbd\x83\xad,\xb8k\xa8_\x19T\xf3\x994\xf9VI\x93\xa2\xca?\xb5\x1d\x896\x1c|\xe6\x8d\xe8\xea\xbcb!w\x92\xf4 m\xd2N\x98\x8d\xd8\xe1*\xd3E\x81\x9cf~\x8a\xd7[\x1e\u0590\x9f\xf7\x05\x85]\\\xdaO\xfd\xbe!\xd2\xe1w\xdbW\xc6\x10\xbe@!\x97\xfdt\xd2`WG\xf7\x04!\xcd\x13\x9f\xa5\xba=\x15\xa2\x15gO1\xed\xf7m\x0eX\x90\xab\x1eNS\x80\xf6*\x18\x83qo\xb6\x14\xbfis\x05\xa5T\xf4\x0fY\xfc\x1c\x8ah\x06\x9f\x85?\u05ec[\xc0\xfb\x9e\xfa\xb4i\xd2=E\x8á\x982U㩱+\xf8\x8c\xa7\x96\x95\xcfvŜ\x83o\xb12\xbb\xd4\xe5N\xdd\x1b\xbd#\u007f8\xf2\xb2\x15^\x91w\xf7\xc28)\x8a\xe2\xe8'\x99\x9c=\xf2\xe2#\x92⚴^\xe2d\rX.Q6t\xeb\\o\xa9<'p\x9e\xeaF\xd7n J:Q\x14\x0f\xfb3\xb05|\xd6\x0e\x9b\x88\xae\x1c\xc2$\xe1\x8b֭p\xbb\xd5\xc6yO\u007f\xb5\x02\xb9\r\xd6P\x04.\x9d\t\xbe\x91\xf2UoA\xba\xeeR\xbe\xe3^vt\f\x1fB\xae\xf0T\x8a\xa3\xcfY\x14YF\xc66^['\x8a\x88|\xfb]9Plv\x12\xf7a\xfe\x97\x88\x1dvB\xf0\xbb~\xff\xf6\xc3\xf1V\xbb18O9\xce)\xf7\xb2=\xaa\xe9\x803\x8dQ\xc1\xb3\x91Α<\xed_ف#\tZ\x14`I\xa6L\x94\t\x9c\x93\xec\xfc\x9et\xef\xddt\bq\xe8ߴ\x9d\xa7TwX\x9c\xa6m\xd90\t&\x96\xe5\xbfY\x92\xb6\x19K[\x99\xed\x85\xda\x11S\x19]\xef\xf6\r_NhƩ\b\\MHAU\xd4;b\xf5p]\xe2j\xa3z!\x98p\x81\x92\xf7\xd0\x15\xd9\xd3$\xa6!$\xdcT^\xbf\x0e\x85\xffV[\xa3\xcbU\xd8\v\xbe\xe5\xb8\n\xa1\x11#5\xd9\xff\xe4\xc8O\x00\xed*l1\x1bT\x15*\x106\xe0\x93\xf0A\xd5\xfc\xb6\xce\xc5)\x9c0.իx\x18t^p(\x18r\x1c߇\x10\xf8\xf1\x1f\x96ݎk\xe0_\x81\x95\xaa)\xfa\xee\x03K\x9e\x15,\xf9\x19\x06\xd9W\x8f^`\x9dx\b\x03\u007f`\x88\xfe\xdf\xd6\x158\xb4\x1a\xe6S\x8aM\xf9u\xd4}\x94\x9dK\xa7\xbc\x83\x18\xec\xc0\b=\xfe$\xb7\xfeN-#\xac\xff\xfcwϺ=$\xd9,\xeff\xcd\x15\xb6DZ\xbb\x03>be0\x13Q\xc7\x03\xe0\xbe@\xb2#,\xe2\xd0\x12zw\x96\xc9{x\x99\x13\xf7\x9a\x1e\\\xf3\xf7\b^ǯ9\xbc\xccw{3\xc7\xeduW\xf7,\xb8\x06\xfa\xd2\x19\xfb\x8f\xd0-\xe2\xb9\x05\b\x11\xdf-\xb2\x8c֛[\xf4\xddz\xae[\x83\xe3D\xb5\xeb\x91;\xf7J\xce[T\x0f\x9c\xbd\x000\x85\xaa\xf1\x06>\xd1\x145+\xb0\xbc\x00\b\xdbqS\xae\u0082\x0f?z\b\xc5\x1e+\xe6\xd7\x02\xa0j\x94\xef\xef\xef\xbe\xfc\xff\x87\xc1c\x80\x12M\xa1ym\x1dR\xfc\u0080\x1b`\xf0\xc5m\vt@?\xd8=\xb3\xa0\xb1\xd6hPZ\x03v\x8fP\xb0\xda6\x1aAm\xe1\x97f\x83Z\xa2Eӂ\x06(Dc,j0\x96Y\x04f\x81A\xad\xb8\xb4\xc0%X^!\xfc\xe9\xfd\xfd\x1d\xa8\xcd\xefXX\x03L\x96\xc0\x8cQ\x05g\x16K8(\xd1T\xe8\xc7\xfey\xddB\xad\xb5\xaaQ[\x1e\xf1\xec[\x8f\xabzOG\xdb{G\x18\xf0\xbd\xa0$vB\xbf\x8d\x80E,\x03\xd2h?v\xcfM\xb7]\xc7!\x03\xc0@\x9d\x98\f\x8b_\xc3\x03j\x02\x03f\xaf\x1aQ\x12\x17\x1eP\x13\xc2\n\xb5\x93\xfc\xbfZ\xd8\x06\xacr\x93\nf10@\u05f8\xb4\xa8%\x13p`\xa2\xc1+\x87\x92\x8a\x1dA#\xcd\x02\x8d\xec\xc1s]\xcc\x1a\xfeUi\x04.\xb7\xea\x06\xf6\xd6\xd6\xe6\xe6\xfaz\xc7m\xdd'\xe9[\xb6\x11x\x03V7\xa7\xd3y4l\x94\x12\xc8\xc6Bx\x8c\x87\xcfh,/\x16\xb0p9F\x83\x1f\x95@\x82\x0e/\xdc\xde\x12x\xd8t\xbcf\xd9\x13\x02\x8b\xd8 \xe5 D\x0f\x89\x03\f\xc0\u007fH\xf8@\x92\xab yr\xbaZ\b\x92\x8b\xa3p\xd2R*\x10J\xeeP\xfb\xd9H+\x14\xceK\x80\r\xeeف+\x1d\xb6\xdeI#\xfc\x8aEc1uz\x99\x85\x92o\xb7\xa8\tN\xbdg\x06\x8d7\xfd\xa6\x112\xad\xca\xc0\t\xffIb\x9e\xec\xa3#$q\xaa\xdb\xf9\xd4\xd2I\x1c\x8d\xcfUl\xb4P\xd26\xee\xfc\x96\xfc\xc0ˆ\tw\x94\x99,\xfc~X\xbb\xae\x94H\x98!\xf2ɚ\xbd\xa0\x88+'J\fL\x03%\x11\x94\x86\x8a\xec\xa1Ӯc\v\xaekS\xdb\xde0\x92vʳ\xa8n\x04\x9a0\x95W/\x9d\f\xb8\x9a\x04\xddR\xc4\xdb҂mP\x80A\x81\x85U:\x8d\x8e%\"\xfb\x96#\xd7&\xb0\x98\x90pC\x1b\xa6\xdb\xd8\fHp\x06Ξ\x17{\xaf,\x88\x83\x1c\x1c(\x15\x1aw\xcaY]\x8b\xe3\xd4&a\x89\xf2a\x92\xb9\x83\u07b5\x85#?\x86\x97:\xfc]ː\x8d][\x90\x92C̶\xec\x00V\xcdn\xfb\xff&b\xa3\xd8\u007f\x01\xd3ޝ\f}]\xa6%\x94rrgﶀUm\x8fW\xc0m|\xba\x04\x91L\xadn\xfe\u007f`\u009c\xcf\xf1w㑯\xca\xf1\xb3TY\x82HTi\xa7\xff\a$\x8aS\x16\x0fAWd\x13\xe4\xd7\xfe\xa8+\xe0ۖ \xe5\x15\xf9M\x16\xf5\x882\xdft^^\x03\x199\xfa\x8eZ\xc5l\xb1\xff\xf8\x95,/\xd3\x05\\3\xf12\x1e\xec\xed\xd7h\xcf\x0f\x15\xf3\x02\\p\x01\x1a\xae\xb1\xf2\x81\x9fG\x87\xcd\ue273\xa8\xde\u007f\xfa\x80\xe5\x1cz \x8f\xf3N6\xf2~\xb4\xd8\xfe\xd4\xc1(\xcf\xddF0}Z\xffƇ\xf4\xae\x80\xc1\x13\x1e\xbd\xc5\xc2$\x10q\x18M4\xe1\xe9\x9c\"\xc7\xc5\x16\x1d\x93=\xe1с\t\xc1\xc2\xc5ѹ\xac\xe0\xdb\x13\x1es\xba\x8d\x10Hk\xe2&\x04A\t\x93\xf4\xc0!\xc2Ŗ\xf2\x91\a.\xf0\x1be\xd1\xf2\xe6 _\x90\xc4\x16q\xff\x82m\xb6d\xeb\x05\xcd\x1da\xdf\x19O\":\x05{^gn\x94\xd4\x1c\x18t\xa7%\x86~\xbf0\xc1\xcbv\"\xcf\xf7wr\xda\x1a\x1e\xb6O\xca\xde\xc9+\xf8\xf8\x95\x9b\x10}\xff\xa0\xd0|R\xd6=y\x13t\xfa\x85\xbf\x00\x99~\xa0;^ҋm\xc2C?\x86\x9c\xc1ܾ\xddy\x0f\xaf%\x0f7p'\xc9o\t\xf8p7\x02~\xbay\xfd0lUc\\\x90X*\xb9r\xaar\x9d\x9a\xc9#;\x13\xa4\xd2\x03\x8a\x9c.\xad\x9d\xd4O\x98\t\xf6\x914\x89\x1f\xef\xef8\x04+\xb0\x8c1N\x17\x99g\x16w\xbc\x80\n\xf5nNq\xf4[M\xf2=o\t\x99R\u05f739,O\xb5\xc7\x16Dw2\x047l+:\xb9\x19\xbd\"\xb1\x17\xbbN\x04䧻.\xefȩXg\u007f,b\x97\x95\xa5\xbbKe\xe2\xfe\f\x89\u007f\x06-Nu\xbf_\x98א\x15s!\xd2\xff&5\xe7\x18\xfa\u007f\xa0f\\g\x9c\xe1\xf7\xeejT\xe0`l\x88b\xf5\xa7\xa1\x19\xb8\x01\xa2\uf049ӫ\x9e\xc4\xe6\x14\xc9\x16\x14^\x91\xab\xed\x89\xc5r\x05\xcf{e\xbcNu\xa1\xd9E\x90\xdc\xc0\xe5\x13\x1e/\xafN\xe4\xc0坼\xf4\n\xfelq\xd3Z\vJ\x8a#\\\xba\xb1\x97\xdfb\x04erbV7w\x05\x9dk*\x93/\x19-\x01\x1a\xd8\u07bb\x92\x99;\xb7\xea,>\xac\x95I\xdc&M,\xe5^\x19\xeb#\x8b\x03\xb3\xf4\x9c(\x16x\x1e\n\xd1+`[\u007f\xf3\xadt\xbc\xd3$\xb17\n\xb8\x12\xd5̼\x84%2\xb6\x111\x0f\x94\x1c\xab\xcb\xee\x04{yz\xe9/:\xdd$\xacp\xc6\xc5\"\xdcZ\xab\x02\x8d\x99g\x91\fi\xbd\x10$l\x03\x84\xcc;0\xfe\xc2p>(\x19[\xbeAJH:Ӕ\xff\xf8\xb5\x17\xbd\xa4\xc3O\u007f/1߹\xeb\x02wf\xab\x8a\x8doƳ\x96x\xebG\xc6c\x12\x00y\xd7@\xef\x1aw\xd4\xf3-\xc8\xc0H\u007f\x045]qy\xe7&\x80\x1f_]\xad\xb7B\x12_b\xb8\xdfƱ\x1d\xd2\xdb\a\xee\xf4\xe6ZD\xcaE\xee5\x0e(w\x1a\xe7&C1\x13\xa4T\xb6\x1fN \xb8\xb5*\xdf\x19\xd8rml\u007f\xa1\xb9L\xd1,\x9c\xfe\xae\x9d\xeb9ɏZ\xbf\xc8q\xfa͏\xec\x05\xb2\xf6\xea9\xe6\x17L^Ŧ\x9a\xbb\x14B\xe0[\xe0\x16P\x16\xaa\x91.\xfcBG\xddM\xe1I\xe0\x05t6\xca\xf2\x04\x045\x94M\x95\x87\x80\x95\xe3:.g\xe34\xfd\xee?3.ނlv*\r#\xd5\x06d\x8b\xf9\x18\xfdD\x91\x8a}\xe5US\x01\xab\b\xf5\xb9n\xcf\xd6gq\f(\xde\xe6r8\xb8N\x8dXE\x87\xaa\x16hsO\xa4\xcfڠcbx\x89\xadb\x0e\\\xa0$0\xd82.&.\xcfO\xdbY\xb8=\xc7\xd7\b\xc2\xe2\xf5\x9c\x88\xbc\xc9W\x0e\x15\x19\x81\xd8Lcq^Z\xd7:\xdfT\xbcטg\x9e-\x05\xa5\xa3yVkN\xbc\xa4^\xdbB\v,\xc6\xe4\xf1\xbb\x89vҾ\x9bh\v\xed\xbb\x896پ\x9bh\xcb\xed\xbb\x89\x16\xdaw\x13-\xb6\xef&\xdaw\x13m\xaeۜ\xb4^Z\x91\xff\xe2d\xe2\xe5\xe2*2\xae\xa7\xe7\x968\x03?dS\xdc\xfa\xafOr3,\xefң\x12Y\xc1᳖\x95\xfb\"'\xc5\x01]\xd2E\xa7JڔK: \x91\xbd}\x02\xfdB\x12\xe67d\xdf\xe6$\xfc,\xa5\xf9\f\xf3L\xdb4\x9b\x98h\xaa\xe2$\t<\xc4/{\xc8\xec\xed\xe7\x90\f\xf3u\x9c\x9d\x1bW\xfaw\xcfA\xcdH\xc5YH\xc0\x99O̝\xc3\xd7\xc8\xf5\x18\"L\x0f\x12F\xff0\xf8ZȒ\x99\u038d\t7Ah\xd9\xe1\xc7\xf5\xf0\x8dU!S\x06\x9e\xb9\xdd'\xb6\xf2\xbcG\xe9\xee\xb0䮟\xf6\x1a\xf9-|b5\xc6#(\r\x92\v\x87\xce\x19n\x1d\xa0\x17~\xab\xbd\vw\xf6\xb9\x9cw?\xf2ri^\x9cA3̐\x99\x10\xd1\xe7^\x19\xe5'\n\xe7\xe7\xc8\xcc'\xb5\x9c\x93\x193\xce{\x99\x04\xba\x9c\x0f\x93\xe39.侼 \xe3%3\xdb\xf1\x9b/\xc6rrZ^\x94ɲ\x98\x10\x98\x99\xbf2\xccL\x99\ayF\xd6J\x16r\x963T\xce\xceK\ty \xb3\xfb\xc8\xceFI\xe4\x99\xcc\x02\x9e\xccA\x99\xcb.Y\x88J\x9df\x9e\xe4\xe7\x94̂v\xf9&˙$\xaf\x97/\xfa\x1a6\xf0\xb4\xa8Y\xcc\x06Y\xb4\x91\xe7\u05f7\x98\xefqN\x96\xc7\"\xc6^\x98\xd1\xd1flL\xcc{n\x1e\xc70Oc\x02hN\xf6\xc6Dv\xc6\x04\xc4ٜ\x8dܜ\x8c\t\xd8\vjw\x96Kf^\xa6?\x04\x86E\xfd&\xfeV\x1c\xf5ҍ)=0\x17\x97,\xf4\xdfF݉\x96\xd1j\x9a7?S\x96'\xb7\xfb\xf3\xcdϪ\x11\x96\xd7\u0085\xf3\x0f\xbcL:\x8dv\x8f\xc7\xf6\xb3\xceߕ\xfb\xccist\x90~\xfbܲ\xe7zdD3\x03\xcf(\x04\xb0\x14s\x9d\xec\xbc\xf0߲\x17j\x85$\xf3\xe9\xc0\x85\x0fV\xc3'\xefW\x9e\x83ݗ\\\xa9\x88\xa7\xddcEP◯g\xb8\x1f\xf3\x06\xa2\xb7eݳ\xbf6\xa8\x8f\xa0\x0e\xa8;\x8ba\xe1;\x02\u007f\xd0L#\xbaĭ ?|\x05\x85\x91\xe1\xdc\x1d8x/\xbd\nK\x82\x1d\xad\xd1\xc1\xa13/ZZ\x93x#?`\xa2k:\xf0\xa1\xdaщ\xf7K\xb6gn\x12\xfeۺ\x0e\xe7;\x0f\x8bj\xfbM\x1c\x88\x97\xbb\x103 s\x93\xea\xf3.\xa0\x16\x93\xe8\xdfʕXr&\xb2\xad\xa8\xbc$\xf9\xb7H\x8e?#)\xfe\f\xa7\xe2<\xb7\"\x1bM9\xc9\xefo\xe2\\\xbc\xa1{\xf1\x16\x0e\xc6\xcb\\\x8c\x05\x90\xa3\xa4\xf6\x9ct\xf5\xac\xcb\xd5\xec\xfb\x85\x9c\xcb\xd1\xe5+\x80\xf94\xf4\x8c\xf4\xf3\x8cˁ\xa5\x95f\xa4\x99\x9f\x97^\x9e\x81\xc37r>\xde\xc8\xfdx\v\a\xe4m]\x90E'd\x91sf_\xbf8\xba\xact\x89z6\x18\x9f\xcbj\xb3L6\xf2\x17\x86s\x8e\xbe\xa8\x8d\x15^\xa8\xd7\xc04M\x85\x94ۯ?\v\xf8\x85\xcb\xd2Ӄ\x98\xaa\xa7\xc7]M,\x97\xff\xde\x1a\x15\x9d}\x96\x06:\xbaT0X3튦m\x8e\xfebҬ\xe1#+\xf6Î\xb0g\x06\xb6JWI\x83鲽\x91\xb9\x8e\xa3\xe8\xc9\xe5\x1a\xe0g\xd5^z\xf5+*\x18^\xd5\xe2H~\x00\\\x0e\x87\xbc\x8c\x01\x92\xcccBy\xa6P\xaff\xc1\xd7{\x18\xf6N\\\xde\xc5R=\x85PM\xd9B\x9f \x1e\x93G\xb8\xff\xe2l\x12W&\xa4\xe8J\xa6\x04\xab#\xfa|\xe3\x8a*?\xbd\xfee\x9e\xb1J\xb3\x1d\xfe\xaa|ݭ%L\f{\x0f\x8a\xae\x05Y\x11/\xd7\xe3\xb7\x17)\x1d\x1a*\x80\x8d\x80u93'՟h\x95)!2s\xfe\xac\x15\v\x9by|\xfc\xd5o\xc0\xf2\n\xd7\x1f\x1a\u007fq\xba\xaa\x996H،\x1b\xf3\x836\xf4߽zN\xc56T\xd8\xf3O\xe3ukty9\xee~\xf6\xac\xd5\x1f\x06U\xc4\"\x8a\x96\x18\xf5KzT\xcf1\xeb\x11ɟ\xf2\xa4C>\x05\xa7WHх,\xdcw5\xaf[\xe6gJjO\x95\x9as\xe5Ֆ\x8b\xcd\xf9*l\xa1\xb4d\xc8\xeej\xb4\xab\xd1\x13*\xb4\xb9\x9a6/\xab7\xe7\x93Q\x06\xe5>\xe7\xe9t{:\xc2\x15u\xd4e\xaf\xde\\[\xf6뙙6\xe1%\xa9H;p~\xa4\xb3d\t\x1a\x96\x80\a\x94\xa0\xa4\xcboq\xd5o|\xe1\xd1\xf1\x98\x04\xd4>\x94\x90@\xd3\xd4B\xb12\x9e\xf0\xa8\xb3B\xb1\xcaG'\xbf\xf4\x01\xf5;3\x03\xb3-\xe4\x96@©\xc0\xf4\x8a\xe5\x06Jfq\x95\x04\x9a%\xfb\x92\xccV\x18>dt\xf3\xdeZ\xf2\vR\xb6\xf2\xb8`\xe0\xd4Ȩ\u007f\xad\xb2L\x80l\xaa\x8dW\xe8,vH\xd1\xef\xa4l\xa0\t\x19O3\xc7\xcbo\x8cK\x8b\xbb\x93\x98\xe2\xe9\xcen#\xff\x9c\xbd\xb3v\xe4\xd4\xceLS\x14h̶\x11\"eڷ\x9c\xfb\xfa\xdbt\xb9|\x8b5\xce\\'/\x02]\"`,\xa3\xe73\x01+4\x86\xedbq\xb3g\xd2@;\x94\xe8\f\x9fT\xbc\xd1;\x86]\xe6ذ\xb4\x97\x8f`\xb1\xc26,L\x10o\xfe{\xbdޥ\xec\x02\xa1v\xbe\x00\"\x8f\xe5_\xa3j>\x13'_k\xaesT\xf9Ƕ#\xe1\xc6\x05\x9f\x1d!\xbar\xbd(\xf8\x8e\x93\x1e$\"\xed\x98ް\x1d\xae\n%\x04\xba4\xf3\xd3u\xbd\xe5a\r\xf9y\x9f\x91\x99ŭ\xfd\xdc\xef\x1b\"\x1d\x9eھ2\x06\xf3\xe5\x15]\xf5V\xcb5v\xe5\x90O\x16\xa4\xdc\xc4g\xa9n\x8f\x85d\xe1\xe0ӕ\xf6\xfb\xc6\x03\x16䪇\x13\xeb\b_\x05c0\xed\xcdV\xecw\xa5\xaf\xa0\xe2\x92\xfe!\x8b߅\"\xe2\xe0\xb3\xd6\xefj\xd6-\xac\xfb\x9e\xfa\xb4i\xd2=E\x8a\xf1@L\x99\xaa\xe9\xd4\xd8\x15|\xc2S\xcb\xcag\xbbb\xe9\x82o\xa9j\xc9\xd4\xe5N\xdek\xb5#\u007f8\xf1\xb2\x15^\x89w\xf7L[΄8\xfaI&gO\xbc\xf8\x80\xa4\xb8&\xad\x974Z\xc3*\x970\x1b\xbau\xae7\x97\x9e\x13\\\x9e\xeaF5v J:Q\x94\x0e\xfb;`k\xf8\xa4,ƈ.\x1f\xc2$\xe1\x8bƮp\xbbU\xdazO\u007f\xb5\x02\xbe\r\xd6P\x02.\x9d\tw#\xe5\x8b\x17\x03\xb7ݥ|ǽ\xce\xd1\xd1\xee\x10\xba\nO\x15;\xfa\x9cEV\x14dl㵱L$\xe4\xdb7\xe5@9\xb3\x93\xb8\x0f˿$\xec\xb0\x13\x84\xdf\xf5\xfb\xb7\x1f\x8e\xb7\xdá\xf3\x98s9\xe5^\xb6'5\x1d\xb8Lc\x94𬹵$O\xfbWv`I\x82\n\x01\x86d\xcaD\x99\xc09\xc9\xeeޓ\uef5b\x0e!\x0e\xfd\x9b\xb6\xf3\x94\xea\x0e\x9bSD\x96\x8dC\xc1Ķ\xfc7K\xdcıD\xcab\xcf䎘J\xabf\xb7\x8f|9\xa1\x19\xa7\"p\r-\nj\xd1\xec\x88\xd5\xc3u\x89m\xb4\xec\x85`\xc2\x05J\xd9[.+\x9e&W\x1aB±\x80\xfeu(\xfc\xb7\xdajU\xad\x02-\xdc-\xc7U\b\x8dh\xae\xc8\xfe'G~\x02hWa˱A]\xa3\x04f\xc2z2>\xa8\x9a'\xeb\\\x9c\xc22ms\xbd\x8a\x87A\xe7\x05\x87\xc2AN\xaf\xf7!\x04~\xfc\x87e\xb7\xe3\x9f2\xb8\x02\xc3e\xac\xdd\xef\x03K\x9e\x15\f\xf9\x19\x1a\x9d\xaf\x9e\xbc\xc0:\xf1\x10\x06\xfe\xc0p\xf9\u007f[W\xe0\xd0j\x98\x8f96\xe5\x97Q\xf7Qv\xae+Q\xddv\tv`\x02\x1f\u007f\xe2[\u007f\xa7VЪ\xff\xfcwϺ=d\xd9,\xeff\xcd\x15g\x89\xb4v\xc7BA\xea{\x81dG\x18ġ%\xf4\xee,\x93\xf7\xf02'\xee5=\xb8\xf8\xb3\x12\xaf\xe3\xd7\x1c^滽\x99\xe3\xf6\xba\xbb{f\xae\x94\xfd\xd2\x19\xfb\xb7\xd0-\xe1\xb9\x05\b\t\xdf-\xb1\x8d֛[\xf4\xddz\xae[\\\xe3D\xb5\xeb\x91;\xf7J\xce[R\x0f\x9c\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5\xe9\xa5x\x0e\xff\xfeϤMה\x13\x1b\x8f\xe2\x87\xfeǵw)d\x94/d\xf1'\xa7:&}\x1d\x84\xbf\xfdc\x92\xaeB\xf1\\>i\xd1\xe4\u007f\x03\x00\x00\xff\xff\x1d\r\x93\v\x97\x1c\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96Ms\xe36\x0f\xc7\xef\xfa\x14\x98}\x0e{y$\xefN\x0f\xed\xe8\xd6\xcd\xee!\xd36\xe3I2\xb9tz\xa0I\xd8\xe2F\"Y\x00t\xeav\xfa\xdd;$%\xbf\xc8v6=\x947\x91 \xf0\xe7\x0f\x04Ī\xae\xebJ\x05\xfb\x84\xc4ֻ\x16T\xb0\xf8\x87\xa0K_\xdc<\xff\xc0\x8d\xf5\x8b\xed\xc7\xea\xd9:\xd3\xc2Md\xf1\xc3=\xb2\x8f\xa4\xf13\xae\xad\xb3b\xbd\xab\x06\x14e\x94\xa8\xb6\x02P\xceyQi\x9a\xd3'\x80\xf6N\xc8\xf7=R\xbdA\xd7<\xc7\x15\xae\xa2\xed\rRv>\x85\xde~h\xbeo>T\x00\x9a0o\u007f\xb4\x03\xb2\xa8!\xb4\xe0b\xdfW\x00N\r\u0602\xc1\x1e\x05WJ?\xc7@\xf8{D\x16n\xb6\xd8#\xf9\xc6\xfa\x8a\x03\xea\x14xC>\x86\x16\x0e\ve\xff(\xaa\x1c\xe8sv\xf5)\xbb\xba/\xae\xf2joY~\xbaf\xf1\xb3\x1d\xadB\x1fI\xf5\x97\x05e\x03\xb6n\x13{E\x17M*\x00\xd6>`\vwIVP\x1aM\x050\xf2\xc82kP\xc6dª_\x92u\x82t\xe3\xfb8Ldk0Țl\x90L\xf0\xb1\xc3|D\xf0k\x90\x0e\xa1\x84\x03\xf1\xb0\xc2Q\x81\xc9\xfb\x00\xbe\xb2wK%]\vM\xe2\xd5\x14\xd3$d4(\xa8?ͧe\x97\x04\xb3\x90u\x9bk\x12X\x94D\x9eD\xe4\xb8\xd6;\xa0#\xbe\xa7\x02\xb2}\x13:ŧ\xd1\x1f\xf2µ\xc8\xc5f\xfb\xb1\x90\xd6\x1d\x0e\xaa\x1dm}@\xf7\xe3\xf2\xf6黇\x93i8\xd5z!\xb5`\x19Ԥ4\x81+\xd4\xc0;\x04O0x\x9a\xa8r\xb3w\x1a\xc8\a$\xb1\xd3\xd5*㨪\x8efg\x12\xde'\x95\xc5\nL*'\xe4\fm\xbc\x04hƃ\x15\x98\x96\x810\x102\xbaR`'\x8e!\x19)\a~\xf5\x15\xb54\xf0\x80\x94\xdc\x00w>\xf6&U\xe1\x16I\x80P\xfb\x8d\xb3\u007f\xee}s:g\n\xda+9\xe4g\x1a\xf9\xd29\xd5\xc3V\xf5\x11\xff\x0f\xca\x19\x18\xd4\x0e\bS\x14\x88\xee\xc8_6\xe1\x06~I\x98\xac[\xfb\x16:\x91\xc0\xedb\xb1\xb12u\x13\xed\x87!:+\xbbEn\fv\x15\xc5\x13/\fn\xb1_\xb0\xddԊtg\x05\xb5D\u0085\n\xb6\xce\xd2]\xee(\xcd`\xfeGc\xff\xe1\xf7'Z\xcf.H\x19\xb9\xd0_\xc9@*\xf3\x92\xf6\xb2\xb5\x9c\xe2\x00:M%:\xf7_\x1e\x1ea\n\x9d\x931\xa7\x9f\xb9\x1f6\xf2!\x05\t\x98uk\xa4\x92\xc45\xf9!\xfbDg\x82\xb7N\xf2\x87\xee-\xba9~\x8e\xab\xc1\nOW2媁\x9b\xdcbSQ\xc7`\x94\xa0i\xe0\xd6\xc1\x8d\x1a\xb0\xbfQ\x8c\xffy\x02\x12i\xae\x13ط\xa5\xe0\xf8\xef07.Ԏ\x16\xa6\xf6}%_\x17\x8a\xf6!\xa0N\x19L\x10\xd3n\xbb\xb6:\x97\a\xac=\xc1Kgu7\x15\xed\x8c\xee\xbe\xc0\x9b\x93\x85\xcb\x05\x9dơM\xceW\xae\x1e\x1er\xee,\xe1\xec\x16\xd6p\xd6s_璛\xe1\xbf$S:\xf1\xc8FG\"trԟեMoe\x81D\x9e\xcefg\xa2\xbed\xa3\xfc\x04P\xd61(\xb7\x1b7\x82tJ\xe0\x05)\x95\x81\xf61\xf5\x194`\xe2\x19\xbf\x11\xcb\xf1\xbf$\x90\xd7\xc8ܜ\xd9Y\xc1ႦW\xb2\x93Fz^\xa8U\x8f-\bE\xbc\x92YE\xa4v\xb3\xb5\xfc\xcf\xfa\x06\x82e\xb2\xb9\x94\x83\xfd\u007f\xfa\x9bIȸ]\x1c\xce#\xd5p\x87/\x17foݒ\xfc\x86\x90\xe7W>-.\v\xbd\xfdc\xe0\r\x94.^ʳIN\xfd\xce\x1cQd\xf1\xa46\xc7\\9\xae\xf6\xfd\xbb\x85\xbf\xfe\xae\x0e\xf7Zi\x8dA\xd0\xdc\xcd_i\xefޝ<\xb7\xf2\xa7\xf6\xae\xbc\x8c\xb8\x85_\u007f\xabJ(4O\xd3\xeb)M\xfe\x13\x00\x00\xff\xff--\nM\xde\n\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdb6\x10\xbd\xfbW\f\xd2CZ \x92\x13\xf4\xd0·v\x93âi\x10\xd8\xe9^\x8a\x1ehj,\xb1K\x91,g\xe8\xcd\xf6\xd7\x17CJ\xfe\x90\xe5\xdd͡\xbc\x89\x1c\x0e\x1f\x1f\xdf.\xbd\u007f[\xffT\xbf]\x00\xe8\x88y\xfa\x17\xd3#\xb1\xea\xc3\n\\\xb2v\x01\xe0T\x8f+h\xfc\x83\xb3^5\x11\xffIHL\xf5\x1e-F_\x1b\xbf\xa0\x80Z\x16m\xa3Oa\x05ǁ2w\x00T6\xf3~H\xb3.i\xf2\x885Ŀ͍~4CD\xb0)*{\t\"\x0f\x92qm\xb2*^\f/\x00H\xfb\x80+\xf8$0\x82\xd2\xd8,\x00\x86\xbdgXհ\xbb\xfd\xbb\x92Jwث\x82\x17\xc0\at\xbf|\xbe\xbd\xfbqs\xd6\r\xd0 \xe9h\x02g\x06'\x98\xc1\x10(\x18\x10\x00\xfb\x03(P\x0eTd\xb3S\x9aa\x17}\x0f[\xa5\xefS8d\x05\xf0ۿQ3\x10\xfb\xa8Z|\x03\x94t\aJ\xf2\x95P\xb0\xbe\x85\x9d\xb1X\x1f&\x85\xe8\x03F6#˥\x9d\x88\xeb\xa4w\x02\xfc\xb5\xec\xadDA#\xaaB\x02\xeep\xe4\a\x9b\x81\x0e\xf0;\xe0\xce\x10D\f\x11\t]\xd1\xd9Yb\x90 \xe5\x86\x1d\u0530\xc1(i\x80:\x9fl#b\xdccd\x88\xa8}\xeb̿\x87\xdc$\fɢV\xf1(\x87c3\x8e1:ea\xafl\xc27\xa0\\\x03\xbdz\x84\x88\x99\xa7\xe4N\xf2\xe5\x10\xaa\xe1w\x1f\x11\x8c\xdb\xf9\x15t́V\xcbekx,*\xed\xfb>9Ï\xcb\\\x1ff\x9b\xd8GZ6\xb8G\xbb$\xd3V*\xea\xce0jN\x11\x97*\x98*Cw\xb9\xb0\xea\xbe\xf9.\x0eeH\xafϰ\xf2\xa3Ȍ8\x1aמ\fd\xcd?q\x02\xa2\xfa\"\x982\xb5\xec\xe2H\xb4t\t;\xeb\x0f\x9b/0.\x9d\x0fc\xca~Q\xcea\"\x1d\x8f@\b3n\x87\xb1\x1cbV\x9e\xe4D\xd7\x04o\x1c\xe7\x0fm\r\xba)\xfd\x94\xb6\xbda\x1a\xc5,gU\xc3Mv\x1a\xd8\"\xa4\xd0(Ʀ\x86[\a7\xaaG{\xa3\b\xff\xf7\x03\x10\xa6\xa9\x12b_v\x04\xa7&9\r.\xac\x9d\f\x8cNv\xe5\xbc&\xa5\xbe\t\xa8\xe5\xf4\x84@\x99ivF\xe7Ҁ\x9d\x8f\xa0\x8e\x95?\x10X\x9fe\x9e\xaf\xdc\fN\xc5\x16y\xda;\xc1\xf2%\a\xc9\xf2\x0f\x9d:7\x9a\xef\xb1nk\xf1\n\x1a\x80\x14\xf7\xf8\xa1\xbe\xc8x\x1d\x03̪w\x16\xc9(b\xa1Ax\x15+\x10\x93:\xc5t\xb9\xb44t\xa9\x9f_\xa0\x82_3揾}r\xfc\xc6;\x16\xb9?\x19t\xe7m\xeaq\xe3T\xa0\xce?\x13{\xcbؿ,r\xbc\x90\x0f\x97\xd4e\xe0\x1a\xc5\xca\xf1\xfa&\x86\x805R\xb2W\x97\xbb\xd9\xdc~\xcb>\xae\x84?\xc9ԕ\xda\x19[\xbe#\x9f\x17\x82ܲ\xa3\x10dJ\xb98\x10\xe4\xed\x11\x1d2\xd2\xd1\xc3\x1e\fw\xb3\x19\x01\x1e:\xa3\xbb<1\xabH\xec\x91\xc8k\x93\xcd\xe6\xdb\xe1K\xf1\x99\x883J\xae\xb2\xc2g\xba\x05\xfcE\xf7\x15˸\xb6@5\x94\xf1\x8bl\x87\x15'\xfa\x06\xe3\xc9\xf1#\xd5:ň\x8e\x87,\xf9\"\x9eNx\xa9\xf3\x8c\xe5\xfa\xc7\xfa\xe33\xf6\xf3\xfe\x18\x99\x9f\x9aʸ\x82&D\xacȴ\xf2|\x9011\xa0l\f\x97d\x94v\xfe\x9c9'j\xf6D\xf1k01\xdb\xec3\x10?\x1c\x02\x8bK\xa2+7\xe0\xf4\xc1\x96\x13\"\xe5ׅV\xd3w\x8d\xb4-B\x83\x16\x19\x1b\xd8>\x16\xbb\u007f$\xc6\xfe\x12\xf7\xce\xc7^\xf1\n\xe4f\xac\xd8\xcc\xc8H\x1e\xd5jkq\x05\x1c\xd35\x95\xcdn\xf38\xfa\x01\xed\xe4\xfa\x8b\x99\xdb\x1e\xd1~[aޞ\xc3\xf4\xee\xfb\xe0@\xcb\x1a[\xb6\x8e+\x95F\xf9\xf6\xf6\xfa\xfe\xcfw\xa3a\x00m\x94F\xe3Dr\xe8\xe1\x1bı\xc1(\x8cY}A\x04\xc3*\xe0\x14\xc0\xd0\x06\xab\bc\xc8#\x86 \x0eaIu\rZ\x94nȒ\xf4\xa9-0\tj\xf3\v\x96\xae\x80;4D&\t\xa6Tr\x87Ɓ\xc1RUR\xfc\xb7\xa7mI\xd7\xe8І9\x8cq\xe5\xf0y\xd7/Y\x03;\xd6t\xf8\x1a\x98\xe4в=\x18\xa4S\xa0\x93\x03z~\x89-\xe0ge\x10\x84ܪ5\xd4\xcei\xbb^\xad*\xe1R\xfc.U\xdbvR\xb8\xfdʇb\xb1\xe9\x9c2v\xc5q\x87\xcdʊj\xc9LY\v\x87\xa5\xeb\f\xae\x98\x16K\x0f]\xfa\x18^\xb4\xfc\x1b\x13#\xbe\xbd\x18a\x9d)F\xf8|x=!\x01\n\xb0 ,\xb0\xb85\xdc\xe2\xc0\xe8\xe4 \xdf\xff\xe3\xee\x03\xa4\xa3\xbd0\xa6\xdc\xf7|?l\xb4\a\x11\x10Ä\xdcbt0[\xa3ZO\x13%\xd7JH\xe7\u007f\x94\x8d@9e\xbf\xed6\xadp$\xf7\xffth\x1dɪ\x80K\x9fԐ\xc3\xec4i./\xe0Z\xc2%k\xb1\xb9d\x16\xbf\xba\x00\x88\xd3vI\x8c}\x9a\b\x86\xf9\xd8tq\xe0\xda`\"%MG\xe45Ʉ\xee4\x96$=b \xed\x14[\x11=\x14\xb9s6]^\x8c\b\xe7\r\x97\xbe\xacw\x9a.\x82\\p\x99\xecI\xd8\xe4\xc0\xa7&\x87\x19VΈ\x024S/\xdb\xef\x19F.\x1b\x1dl1\xa3pD\f\xf4I\xc5\xf1\xcc=n\x14\xc7\x1cl\xda\n\xaefA[)\xe3#\u007f\xd4I9?\x85>%\x9f\x05L+~\x06W<\x91\x81\xc1-\x1a\x94%&\xc7u*\x9d\xc9 \x1b&\x1as\x8cǕ\x02Nx\xf5,ⷷ\xd7ɓ'&F\xecn~\xee\x19\xfeз\x15\xd8p\x1f\xe8Ο}q\xbd\r\x87y\x9f\xe6\x140\xd0\x02Cb\xda\a\t\x10\xd2:d\x1c\xd46K\x91\xca' \xc37\x18w\xbc\x0e\x1e,\xba\xcaCh!\xde\x03#\xdf)8\xfc\xeb\xee\xdd\xcd\xea\x9f9\xd6\xf7\xb7\x00V\x96h}^\xee\xb0E\xe9^\xf7\xa5\x02G+\frJ\xfc\xb1h\x99\x14[\xb4\xae\x88g\xa0\xb1\x9f\xde|\xces\x0f\xe0Ge\x00\xbf\xb0V7\xf8\x1aD\xe0x\uf593\xd2\b\x1b\xd8\xd1S\x84G\xe1j1\r\xa6=\aH\xbd\xe2\xb5\x1f\xfdu\x1d{@P\xf1\xba\x1dB#\x1ep\r\xaf|Zs\x80\xf9+\xd9\xceo\xaf\x8eP\xfdS0\xedW\xb4\xe8U\x00\xd7\xc7\xe1\xa1\xd1\x1d@\x06\xcb3\xa2\xaa\xf0\x90UM?\x1fT\xc8U\u007f\v\xca\x10\a\xa4\x1a\x90\xf0\x84Iz\xc1Q\"\x9f\x81\xfe\xf4\xe6\xf3Q\xc4c~\x81\x90\x1c\xbf\xc0\x1b\x10\xb1\xd8Ҋ\u007f[\xc0\a\xaf\x1d{\xe9\xd8\x17:\xa9\xac\x95\xc5c\x9cU\xb2ه\xf6\x881\tʄyp\xcdL\ueffa*\x13C;C\x88\xf6\xcb\xd8\xf6[2\xc9\xe9\xffVXG\xe3/\xe2`'\x9ed\xbe\x1f\xaf\xaf~\x1f\x05\xefċl\xf5H\x02\x1etd\xd8\xe58\x93\x98\xbd\x1f-N\xa9c&c\xed\xd7<+3t\xacʤb\xc3\xf6䩄\xed$\aƭ\x18VY`\x06\x81A\xcb4I\xee\x01\xf7\xcb\x10\xe25\x13\x14\x9f)\x04\xf7}\x0e`Z7\"\x1b\x8ac \x8fIh\xe4\x04\x15ڬ\xb2\xc7\ue795ð\xafsF\n\x1f\aK\x93\f\xcet\x96\\\x9d\xb3\xd4Q\xbfi\x8e\x16e\xd7Ρ,\xe1Ai\xc12\xe3\x06\xad\x13ef\xe2\xd5<\xd38!\xac\xc0\xcb3<\x88-\xe8L\xf1\x12E\x112\xbd\xbe\x80\xf1]\xc7\\\x85p\xbc<8\n\x91*t\xca[\xc7\x10\x97\xf9Rr\xb2\x86J\xabɐV|1ed\xa6\xf3\x98&G\x9d\xd1!\xd2y}\xed\x1b\xdeϨ\xb0C#?\xf24\xf8S\x97\xda\xfbTL\xbc\xb4\xc6.\x15\xe5\xe9㧕\xd3⽜\xef\xf0\xed,ã\xba\x8b\x96\xacw\xd0\xf6\x8fg\xe4\x8ad\x18\x90\v;}\x04#j\xc8}\x12M9\xfe\x96\x89\x069\xa4\xb7\x9d\xe9\x9e\f\xd5!\x95\rn\xc9\xdd\a\xd3K\xa5i\x84\xd7'\xaa5\x82\xf5}\xa2\v{\x82fg\x91\xfb\x9eF\x86\t\xf3\xe4u\xabL\xcb\\\xe8k.\xb3De\xd74l\xd3\xe0\x1a\x9c\xe9\xe6\xd3',\xb1EkYu\xce\x14\u007f\x0e\xabB\xc5\x1e\xb7\x00ۨ\xce\xf5%\xfb\xc8=^بS\xcf\xeb\x1ad\x8b\xe1\xb1:3*VlLڛ\xc6\xef\x19:\x82Ã\xa0G\xb5\xc1|\xd0\u007f\x89O\x00\xf0\x0fZ\xe7\x10Қ\x9c\x81\xf5\xde뤅\xc1\t\xa7|\x83\x8f\x99\xd1\xd9C\xdcp\xf22\x99Lf\xeeGo\rϺ\u007f<\xe8\x1c\v\xe22\xa8U\x93\x8cY9ր\xec\xda\r\x1a\xe2\xc3f\xefЎ\xddy\xae?\xe3\xeb\xba\x03\x1b\a\xfb\x93\xfc\x02\xa5X\xaa\x96L\xfa>*Y\x97S\xc0\x85\xd5\r\xdbg\b\xa7\x8b\xf8܍\x8c\x8b\\\xc0A\x9f\x93Qk4~\xea\xb9}%\x8f\xe9J\xc9#\x95F\xb2g!\xdd_\xffr\"\xd3\x13\xd2a5\t\x0eq\x9e\xd8\xf9\x03\x9d\xf2uN8\x91\xc4Xɴ\xad\x95\xbb\xbe:\xa3\x05w\xfd\xc2d\r\xb3\xe79\xec\xa9EUȉ\xaa\xf7-\xcf2\xd5\xf1\x13\xf09\xa8\xa3\xc5g\xa2P||\xceŠ;\xd4̐\xa5\xfb7\x81\xcb\xe9\xa3\xd5k\xb0\xc27:)\xf3\f\xa9hhCX\nN\x94Z)\x83\x19\x97\t\xf3\xb02\n\"c\xf8\xbfg\xfc\xc8\xea\xc9l\xd0#\xe7\x03ڱY>\x1c\xe96\xfdC\xd0\x1a~\xfdmqHlXI\xc5\x13\xf2\x9b\xe9\x1fYĔ3\xfdՄ\xffY*\x19*\t\xbb\x86O\x9f\x17\xe9\xd9\xf2>\xfd1\x04\r\xfe/\x00\x00\xff\xff\xb0\xddǼ\x99\"\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\x1b\xb9\x11\xbe\xf3Wti\x0f\xcaV\x99\xc3]'\x95\xa4x\xb3\xa5lJɮ\xac2e]\\>\x80\x83&\a\xab\x19`\x82Ɛf\xb6\xf6\xbf\xa7\x1a\x0fr^$%U\xb4\x9e\x8b-\xa0\xd1\xf8\xf0\xa1_hN\xa6\xd3\xe9D\xd4\xea\x01-)\xa3\xe7 j\x85_\x1dj\xfe\x8b\xb2ǿS\xa6\xccl\xf3\xe3\xe4Qi9\x87\xab\x86\x9c\xa9>\"\x99\xc6\xe6x\x8d+\xa5\x95SFO*tB\n'\xe6\x13\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv֔%\xda\xe9\x1au\xf6\xd8,q٨R\xa2\xf5\xca\xd3֛\x1f\xb2\xbfe?L\x00r\x8b~\xf9\xbd\xaa\x90\x9c\xa8\xea9\xe8\xa6,'\x00ZT8\x87\xdaȍ)\x9b\n-\x923\x16)\xdb`\x89\xd6d\xcaL\xa8Ɯw][\xd3\xd4s8L\x84\xc5\x11Q8͝\x91\x0f^\xcfǠ\xc7O\x95\x8aܿG\xa7\u007fV\xe4\xbcH]6V\x94#8\xfc,)\xbdnJa\x87\xf3\x13\x00\xcaM\x8ds\xb8e(\xb5\xc8QN\x00\"\x01\x1e\xda\x14\x84\x94\x9eRQ\xdeY\xa5\x1d\xda+V\x91\xa8\x9c\x82Dʭ\xaa\x9d\xa7l\xaf\a\xcc\n\\\x81\xbc\xa5\xa7[(\xad\xf4\xda\x0f\x05\b\xe0\f,\x11\"\x12\xe9\x95\x01\xfcJF\xdf\tW\xcc!c\xe2\xb2\xda\xc8L'\x9dQ&p~\xdb\x1bu;>\a9\xab\xf4\xfa\x18\xb2\xff3\xa8\x0e\x9e;#\x9f\x88\xe4\xbe@/\x93\xd04ui\x84D˛\x17B\xcb\x12\x81-\x17\x9c\x15\x9aVh\x8f\xa0H\xcb\xeewu\x17ɧ\xa4\xaf5\xf3\x1cv\x9eCE\x90\xedl\xff\xd0\x1e:\xb7\uf751q\x01D\xa3\x06r\xc25\x04\xd4\xe4\x05\b\x82[\xdc\xcen\xf4\x9d5k\x8bD#0\xbcxV\x17\x82\xba8\x16~\xe2uq\xac\x8c\xad\x84\x9b\x83\xd2\xee\xaf\u007f9\x8e-.ʜq\xa2|\xbfsH\x1d\xa4\xf7\xfdဖ\x9dm\x1d\xaf\xff\x9b\xc0]2\xa4k\xa3\xbb\xbc\xbe\uf34e\x81m)M\x818\x1b\x04ю\xd6w\xeb\xae>)\\\x18\bӛ\x1fC(\xcb\v\xac\xc4ם\xb8\x01Nv\xa0\bD\\\x1aNq :\x85\xec\x8f\xffX\xdcC\xda\xda_F\x9f}\xcf\xfba!\x1d\xae\x80\tSz\xc5A\x97/qeM\xe5u\xa2\x96\xb5Q\xda\xf9?\xf2R\xa1\xee\xd3OͲR\x8e\xef\xfd?\r\x92\xe3\xbb\xca\xe0ʗ\x18\x1c/\x9b\x9a-Wfp\xa3\xe1JTX^\t\xc2W\xbf\x00f\x9a\xa6L\xecӮ\xa0]\x1d\xf5\x85\x03k\xad\x89T\xc1\x1c\xb9\xaf~U\xb2\xa81\xe7\xebc\x06y\xa9Z\xa9\xdc\xfb\x06\x87\x1f\x10\x03\xf9\xac\xa3z\xdcu\xf9[\x8a\xfc\xb1\xa9\x17\xceX\xb1ƟM\xd0\xd9\x17\xeaa{?\xb6&\x81ӭ\x9c\x17\x94\x03\x05ɁR\x802-\xde\x16h\xb1\xbd\xc6bmH9cw\xac8d\xcbl\xa0\xe1\xc8E\xf8#\x1by\xe6\x18\x1c\xee\xbdCX\\\xa1E\x9dc\x8a\x10\xa7*\x99\x91S\xb4\x12\xfa\x10\xe2q\xea\xe1D\xf4\x1c\x05\xfc\xee\xee&E\xcc\xc4p\x84\xee\x86\xfb\x9e\xa1\x87\xbf\x95\xc2R\xfa\x84r~\xef˛U\xd8\xcc\xc7\x0eg@@\xad0T\xa4\xfb`\fJ\x93C!\xc1\xacF5\xf2\xa3\x01\xd8\xc1,\xc6\x15oB\xa4\x88!\xe9\x10\u0099z\x10\x1c\xa3\x94\x84\u007f->\xdc\xce\xfe9\xc6\xfc\xfe\x14 \xf2\x1c\x89|\xbe\xc6\n\xb5{\xb3\xcf\xd9\x12IY\x94\\\xb8`V\t\xadVH.\x8b{\xa0\xa5\xcfo\xbf\x8c\xb3\a\U00013c40_EU\x97\xf8\x06T`|\x1f\xfe\x92\xcd(\nt\xec5\xc2V\xb9B\xf5\x93֞\x01\xb6\xaex\xec\xad?\xae\x13\x8f\b&\x1e\xb7A(\xd5#\xce\xe1\xc2W\x82\a\x98\xbf\xb1c\xfd~qD럂\x03]\xb0\xd0E\x00\xb7\xcfwm\x8f<\x80t\x85p\xe0\xacZ\xaf\xf1P\x88\xf6?\x1f\xbc9$~\x0f\xc62\x03ڴTx\xc5|{!\x1e\xa1\x1c\x80\xfe\xfc\xf6\xcbQ\xc4]\xbe@i\x89_\xe1-(\x1d\xb8\xa9\x8d\xfc>\x83{o\x1d;\xed\xc4W\xde)/\f\xe11f\x8d.w\xa1\xda\xdf \x90\xa9\x10\xb6X\x96\xd3PoH؊\x1d\xb3\x90.\x8e\xedM@-\xac;i\xad\xa9ʸ\xffp\xfda\x1e\x90\xb1A\xad}\xbc\xe3\xec\xb4R\\5p\xb9\x10r\x9e\xb7\xc6A\xd2L\x1f5\xc1|\x9c\x81\xbc\x10z\x8d\xe1\xbc\b\xab\x86\xb3Pv\xf9\x12?\x1e\xa6\xfe\xf4\x8d\x94\x00\xfd\xc0\xf1͒\xe8\x13\x0f\xe7+\xd5'\x1c\xae\xfd\xd6:y\xb8\xc7f\x89V\xa3C\u007f>ir\xe2\xa3\xe5X;\x9a\x99\rڍ\xc2\xedlk\xec\xa3\xd2\xeb)\x9b\xe64\xd8\x00\xcd\xfc\x93y\xf6\x9d\xff\xe7\xc5g\xf1\xaf\xeb\xa7\x1e\xa8\xf3\xe8\u007f\xcdS\xf1>4{ѡR\xad\xf8\xf4\x87\xdf~\x9f\x1cҝȹ\xd6Ey\xdb\xff!8V#\xe9w]\xffgnt\xf8!\x96\xe6\xf0\xf9\xcb\x04b\xab\xf1!\xfdX˃\xff\v\x00\x00\xff\xff\xd7w>\xba>\x1f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xdc\x18\x93Z\x98\x92\x9a\x01fAW֬#D{zSI\xa4\xbd\x16ĺv\xacV\x95\x1f{h\xe0:\x14\x8fS\x04V\xcc`\x0e*\x88}%Є\xb5r\xc7\xfeV\xb1\\\x8d\x82n\x90\xf7\xae\x86`+\x14`P`f\x95\x1eR2\x85\x9e\xfeIQ\x96#t\x8c\xa8;\xfc\xb7\x88M\x80\x04\x12\xf3\xe7-϶\xde\v \xd9tp Wh\x9c\xe6 Ou?\x86$\xcc\xf1>,2\xa5;\xdag\xe6L\x1d\u008b\xe9\x93\xf6Iз\xed3\xa3y\a\x9a%\xbc\x8f\x9a\xce\xf6\xf9\xf7$lmJN\x10ڻ\xc1\xd4\xd7\x15Z\x17U\x91\xb7\u007f\xb7\x06,J\xbb\xbf\x02n\xeb\xb7s\x10\x99\x10\x9d\xf5\xffƌ9^\xe2\xef\x0eg\xbe\xaa\xc4Ore\x0e\"q\xa5Y\xfeo\xc8\x14g,\x1e\x83\xadHfȯ\xddYW\xc0\xd7\rC\xf2+XsaQ\x1fp\xe6E\xe7\xe55\x88\x91b\xef\xe8)\x98Ͷ\uffd0gc\xda\fT\"]\x0e'{\x9f\xb8\x0e\x12\xfa\x86y\x06.\xb8\xf8\x95k,|\\\xfc\xc9Q\xb3}\xe3\x02\x8aw\x1f~\xc4|\x8a<\x90&y\x03D\xde\x1dl\xb6\xbbtp\xf4S\xd1\b\xaeO\x134\xf9\x8c\xc7\x150x½\xf7X\x98\x04b\x0e\xb3\xceߍ\x86OC\xe2\xb8ԋw\x8fq\xef\xc0\x84\\\xca\xec\xecTQ\xf0\xcf\x13F\xfc\xfd\xd8\xd3# \xed)D\xb8\x9e\x92\xf4\xc2\x11\xc2E\xde\xe9\xc4\x03\x97\x17\xabu\xd1\xb7]o\x87@\x9c\xd5ۡ\x05\x1aCwJj\xe6\xae\a\x80Le\xed8\xbb\xbd7Rs\x84\xe7\xb3B\nP1\xf7I/2\x9f\xc1\x8f\xf6\xbdK#e\xf0(vǻ.I\x9c\xedNH\xcf\x12}Y\xb4\xed\n\v\x97\x14\xd4;\\T\xf2I\xaag\xb9p1\xa5\x99\xcd\xd67\x8b\x9f\xac8\xbe\xa6\xd2\xe8\x8bW\xba\b\xd4\xf6\xf7\fJ!\x99\xcdG\x05\xc6SR0\xa7\x86|\x1b\xebȟ\xb3\xbb\x98Z\u007fbr\xa89\xde\xfa\xfe\xd3\xd4ަ\xbb\xf8\xac\x8e\xff\xf0\xbcE\xbbE]7\xb6.\\\x0foL\xad\xb6\xa5\xc9\xd6\x15n\x9a\x9dH~jo\xca7\xe1\x1d\xb4?\xc5}eY\tqE\x82\xcd*a}+\xaa\xae\"B\x94\xd4\x03\xb4RJ ;l\x8bM)\xa2ϕ\xce\xfb\xfd`M\xe9\xban\bS\xf5\"\x11\f=/}\xd7g\xb7.ۯ\x81\xbb\xecO\xbdӿ\xbcU,\xa1\xbc=SԞn\xa0\x9b\xa2\xd7Pl\xba\x14ke0\x8c\v\x9d\x95\xdf\x14\xf9f\n\xd1\xe3\xe5\xe7\x90lE\xcbvo\x97\xfd\u007f\xac\n\xc5h\x97Y\x88\xa0\xf2\xbcm\xf2\x04\xce\xf2ʜ\xefx^1ѓ\xc0\x0e\xcdZ҂\xd2 \xb9\x88ա\x88\xe6\xf5\xfc\x1e\x8d\xe1c\xe9\xf3\xd1G\x9f\xd5iw'\xadf}r\xa5\xba_\x89\x1e\xd1\xe0Ǧf\xd3[\xf2\xd2k\xd1\xd3\xc5\xe3c*Ї\xf5\xe5Q\xa0\xf3u\xe7\x14Ou\xa6\xc6|Be9\xb1\xab\xe8\xc5\t\xe8\x94\xda\xf1I\x15\xe3\xd9ƛ\xc4:q\xbf\x02<\r\xf2\x88\xeap\x12q\xe6+\xc1G\xd7\u007fC\xbdu\x12\x8f\xe4\xaao\xa4\x9e;\tx\xb4\xd6;Uŝ&y\xa4\u009b^\xbb\x9d\x04\xed\xea\xba\xf3\x15\xdb\xd7\xeb\xcbz\r\x17y\\\xd5\xccV]_\xe4B'\xd4U\x8f\xa9\xa6\xceR\xec\xc4\xcaiS\x19\x1dY\xf7\xd8zi\xbf\x1e:\x024\xa5J:R\x05\x1d\x818Y\x1bM\xad}\x8e\xc0\x9e1\xbb\x93R2\xf1g\xe3u\xff\xc6ʒ\xcb͐\xf3\xa9\xf21)\x1b\x83\xd2iw͞pt\x9d\xe3^X\x11[\xd2\u007f\x90\x18\tA\xea\xa4\x13\x97V-\xe1\x9d\xdc\x0f\xe0\xbaf\xe8\xa8\xcb\xdd\xffb\x85\xb6\xf5̅\xe8~\x95\xe1\xc0vA\x85\x0f\x9cL<\x10\xa6\x81c\x1f3E\x99\xa2t\xcfߝ\x8b8>\x1e\f鈴\xa6\xfd\xe7\x98\xeb\xcc\xed\xf6D\xff\xb9\xa8\x84\xe5e\xf4\x10\x97Z\xed\xb8K\x8amq\xdf\xd0\xf3\x0f徇X\xed\x1d\xa4\x8f\x0f\xcd\xf9Z\x1e\x84\x02,v*\x9eQ\b`f\x88~\xe6\xbf\t\xcc\xd4\xc2}\xe6C\x9c\xac\xe5!|;x\xe5\xce`,@\x95\xf5\xd7j\x05\x81q\xdf\x15\x9aH\x02`ԺL{\xb8\xde\x19w\xef\xfe\xacP\xefA\xed\\\x154\xb8<3\r\xc7^S\x98J\xb4\x1d\x1eA\x01\xfa/Q\x0f<\xffVc\xc0;\xe9mp\x14\xec\xc1\x1e\x1d\x1cRZm\xb4C\xfa\x99\x02\x99\x91\xa1Q\xa8R5\xb3\xe3\xf20ijR\xbbu\xcf\x1b\xfb\x1c\x1f\xfd\xcc\xfa\x1dg\x89\x80N\x8f\x81&@\xa6vߦe\xecg\xbbm\xcf\x15\v\xcdEC\xc9n`Z7\xed9\xbah\x8f\xe8\x9e=\"*:..J&SJ\x97\xecY\xa2\xa33\xc6G爐N\x8b\x91f@\x1et\xbf\xa6\xf4\xb5&\x15\x99\x92K\x14)E\xa5\xf9\xba\xe6t\xbfjB\x9fjB\xf1cn\xa7\t\xfd\xa8\xc7\xf5\xa1&\xd0\xf0L\xd1ә\xe2\xa7sDP獡f\xa3\xa8Yə\xfc\xfb\xe4\x1cy]M\xfd\xa0r\xbcW\xda\xce9\xfc\xf7\x87\xe3#\x15\xacN\x10\xa4D\x0e\xb2\x1e\x1aA\xca\xf9\xf2\xc1\x8f?\r\xa9x\xb1)\xac\u007f\xffy\x0e\x9f\x87f\xe04\"\xe4\x92\xd6\xf1Y\x04\x0f\x9a\xefp1\x92\x95f\xab,|\xb7\xe3,\\(\xa2\xaa<\x04!\xfa\xfbs`\xf9h\x99\xad\x12\x11\xf5c{\xb8\xf2l۩\xe7G\x10\xb2\x91\xb3\x05&4ڣ\xd1eq\x05\xcd\aߩ\xc2\xc4\xcf\xe2\xae\xea\xcfU\x99\xb4\xee\x97N\xf5'i\x1d\u007f*\xb2҈\xac5\x1e\xd7Z\xa9\x1e\xcbL\x98\xa6\xfe\f\xc0&\xba\xc0+\xf8LC\x15\"\xc1\xf4\f\xa0\x9a\x18\x0f\xbd\x02\x91\xa6L*\x91}5R94\xd7:+\xf3@\xa2\x15\xa4h\x13#\vǤ\xb8s\u0095\x16\xf4\x16\xdc\x0e\xdb\xe3P\xf9\xcdj\xf5U\xb8\xdd\x15\xac-\xb7[\x17;a\xc3WO\"\x0f\xa0\xaar\a\xc2\xcd:#\xd5\xe3\xd0h\x1f\xe0\xdah\x05\xf8\xbd0h\teH\x99\xb3\xea\x11\x9ew\xa8\xc0i0\xa5bT\xfe!\x92\xa7\xb2\x18@\xa4\xc0d\xddó¤[9\x87\xcb\xfd\x0e!\x13ց\x939\x82\xa8\x06\x84ga\x19\x87\xad6\xe0v\xd2\xceӄ\x80t\xb0\xf5\xe8|\xeaW{\x84R\xe1\xb0B\xa7\x05*H\xf5\xfaH\";0?\x9a\x1a\xf6\x00?|\xfe\x88\xe9\x1c\xc9 ^R\x8f&\xf5\xa1\xe7\xe9\xb4Q\xe0\tF\x81lM\x8aݴ:\xc6\xf3\x1b\xa3\x17 \xe0\t\x0f\u07b3\x1a\f.\x87\n\xb1V\xd4 \r\xf2.-\xab\x91'<0\xa8j\xdb5\n\xde\x12Q\xf1\xe5\t\x0f\xb1M{D%\xfc\xaa\r\x1fO]\xaa\xe0Y\xc4,\xa5\xa6\xd4D\xad\xd6\x0e8\x1d7YX\xa6\x94B\t\x14?q\xda5\xc3:g\rOxxg=\xfbh\xd5\xecd\xb1\x80\x02\xa4\xb0\xc1\"\xaf\xb0\xb0\xc9\xfe 2\x99փ\xf1:Y\x00\xf1V]\xc0g\xed蟛\xef\xd2\x12\x8a*\x85\x8f\x1a\xedg\xed\xb8\xe6UI\xec'q\"\x81}g^\x96ʛ\x05\xa2ˢ\xf1\x1b\x1c\u0604\x92\x88\xd6l\x93\x16n\x15\xc5g\x9e>KشÀ\x9cG+/-o\xd3+\xadVl\xa6\xc3h\v\x80\xb6\xf1\xaaX\xa5M\x87S\x17\v!\x0e\xa2X\xa1wO\xd6\xca\u007f9:\xe0\x98*\x06\x8bL$\x98\x86\xedJ>M\x11\x0e\x1fe\x029\x9aG\x84\x82\xecF\xbcP-\xd0侜 \x85\xf1\xaeE(\x95Y\x188\x1c\x18*+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\x1c\x9dL7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xeb_fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03g:d\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\x91\xe5\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca9\x05KB\x01\x8a\xb5\x83\xd7B\x9d\xeb\x93wr\xe1\xe7f\x11-Ӆ\xb6#\xc7N#h}\xd5\xd6\xf9\r\xc0\x8e\xbb=\xb0C\x18\x13\xfdU\xbb\x86 \xb6\x0e\rX\xa7M8\xe5&\xb5\xdb\xdb '\xce\xdby\xde\x13\xab\xeb\xddH\x0f\x98\x82\xcc\xf3FCx\x9d~\ue3ff\xe9\xff\xf30\x13v\x96\x18vat\x82\xd6\u038bR\xa4\xe5\x98ٰ\xad7k\x85\x0f\u07b6Q\xaa9f+9\x94e\xae8\x91\xf6\x84\xc0\xe6\xe6{kߙ\xd4\x10\xfd\x8e\x11\xe5Sp\x04\u0380\xcbs\xd1ϸ\x88F\xf7\xda\xf7\x0e\v\xb0\x02\xe6\x03&\xf3X\xb2RY\xe67W\"\xf9Ws/mi6Z\x9d?4oU~ \am\xd9\x05\x81\xd9|\xb3\x18\xa4!&\xcbl8\u007fl\x06\xea\x92ܲ\xd8\x18<\"\x8f,>{,\x8e<\xc0װcsƢ\xbd\xb6\xd8\xfc\xb0\xd7\xc9\n\x8b\xcc\x05kex͂<1\x03,\x9a`q\xd9^\xd19^\xaḓyjMdv\r\xe7k͂\x1c\xca\xe7\x8a\xc9Ҋ\xc25:7\xabθ\x9a\xdfI\xfc\xa1\x8c\xac\x97\xcf\xfd~I?\u007f:\xbf**\xab**\x16\x98\xc79*oji\xb6T\x14U\x97fF\xd5YO\x13\x03G\xe5C\x1d\xe7:MMe6\vj<\xc3i\n\xecP\xeeSD^\xd3\x04\xc8v\xc6\xd3b7`V\x9af\x1a\f?\x95\x10ʼ\xad\xcd\xfe/$\xf0G'\xadM\xc7\x05\x8e\x89J\xbe\xf4\xba\x10\xef\x83\xd77\xe4V\x8f\xc7x\xde\xd9>\xc1\xad\x1e\x01y\xbb\x85\xbc̜,\xb2\xd6[\x05n\x87\ax\x96YF\xea\xfc7\xcdW/7\a\x86\xf6\xe5[-\xc0c \xbb\x01\x82\xb0\xf0\x8cYF\xff\x1eQ!\xf1/\x83$z\x85dsƷ\xc1\xab\v\xf2ճ\"\x17>\u05cf沈=\xcb\tR\xb8W\u007fB\xf85\xed\xecz\x1f\x9d\xeb~/\xd1\x1c@\xef\xd1\xd4^ͨ\x985\xf7\x95\xaa\xa5iˬQ%\x95N\xf2\xef\xd3tU\xcb\xf8j\xa8\x174|P\xde\xcc\xf6qeX\xa4C\x9a\xe0hJuR,4\x06B\xe9\x1a\xc2H\xff\x18_z\xc9\x05\x9e\xd7\b\x95^\"X\x8ar+^#`z\xad\x90iiд\xe4\xe82\xea\x02\xcek\x84NK\x82\xa7E\x1e`\xfc\x05\x9b\u05faX\xf3\nA\xd4\xc9a\xd4\"\xd2\xc5^\x9cY\x1cLE\xcco\xe6\xa2̑\xc7\x15\x01r\xf4\x82\xccp@\x15\x01\xf1\xe8b\xcclH\x15\xb3\x0e\xfaA\xd7\x0f_s\x89>\xc6_t\x96\x14{\x04\x1fw\xcc3\u007f}%\xf2\xdaJ\xe4!P\f\xf6\x91\xd7S\x96_K\x89\xa4\xf3\x89\xc1\xd6\xe4Б\xd7O\x16\x85['\x06\\\x93\x10\xa7\xae\x9bL\x87\\\xd3\xdbi\xfdk&'\xb8\x13\x11\x126\xdb\xe4\x87O\x04\xb4I\xd1\xcc\x1e\xae,\x11\xcdY\xa1\xec\xc5D\xdd\xf1{\xaf\x0e\x847\xb1\xa8U\xfb\xe0f\x8c;\xba\xbe\x05\x9f\xc0/R\xa5\x9e7$\x84-\xff\x82_O\xe4{2\xb5\xe33.F\x8d\xb7\xd9;4\xb2X\bR\xa3\x1c\x14\xf1\xe1\xb6]ÍHvu\xc3\x11\x88<\xf2NX\xd8j\x93\v\a\xe7\xf5i\xdce\xe8I5\xe7k\x80\x9fu}\x10\xdaz\xe5f\x04\xae\x95y\x91\x1d(\xfa\x81\xf3.\xa0\x1f\x13\x9dQ\xf1\xb3\xd5KpՃh\x11\x11\xf0]\xb7\xc7\xd0K\x84\xd5shI\xa6˴\x1ea\x82\xddB\x1d\xe0\xeb\x03{S\xfc\bT\xd2<\x96U\xf9J!\x12\uef655\x02r\xecA\xc1E$\x1b?\x1c\xb6N\x1b\U000487f4\u007f\xf31\x86f\xdd\x1e\x9dg?+]\x15RE\xaa\xbb_\xa3\x92\xec\xe7\xd6\a\xd8d\x90U\xab\xad9K'lǔ\xd8\xcc:w.\x8b\x98\xdc\xfd\xfd'?!'s\\\u007f,\xfdA\xfd\xaa\x10\xc6\"Q:L\xd4wڌ۹\x9d~\x86LWt\xf8G\u007f\x1e\x069c\x8ds\x02N\x9a;\xf3\xf4a ]\x8c\xc8?\f\xf7l\x05\xb2-&N\x1d\xed\xeb\xed(,a\xadN$\xeb\"\xde\x12\xe2D\xb1\xd7{*nʢL\xa8\x8c\xd2\xe2\x97g\x85\xe6[X\xa8\xf6VyNͼ\xc4\xf9\xebQ\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@ֿR\x19\xf6\xb6\xa4\xad\xdf&=&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf89\xd0U\xfdB\xe9Y\x04e\xfd+\x9c1\x8f\xcf\xfa\xe7:\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xda\xf3\xb3\xcds\xde3\xbcl\x1e\xf8n\xa2\xfd\xd9\xe7\xc4\a\xf8W? ;\xfa\xa2\xaa\xb7\xae\xfe\xb9\xef\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c{l\x97\xda\xd4I\xbe\x15\xa1\xb9cx\x9f\xf0n\f\xf5\xe1\xac\xcd\x15|\xc6\xe7\x81\xda\x1bE\x938>S\U000e9658\xf2\x1e\xc1\xd0\xd3ۓS\xdc\u05fd8/v@[t\xd5\\\xafy/\xe1FdY\v\xa2ρ\x1db\xeb?˭\xdf\xc0IhN\xffr\xd4bTqM*\xad1\x855\xb8\xa4\x8e*-\x9a=\xa6-!\xa9lx\xbb\xa6\xdc4\xcfE\xc2\x1f\u007f\x9e5\xabR$\t\x16\xaeJ\xecj\xff\x99\x83\xf3s\xfe\x11\xfe\x8a\x01\xffL\xb4\U0008edbd\x82\xff\xfa\xef3\xa8\f\xf0C\xf8S\x05T\xf9\xbf\x01\x00\x00\xff\xff\x16'-~\x14b\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec=]s\x1c7l\xef\xfa\x15\x18\xf5\xc1\xed\x8c\xee\x14O\x1f\xdaћ++SM\\\xdbc)zi\xfb\xc0\xdb\xc5\xe9\x18\xed\x92\x1b\x92{\xf25\x93\xff\xde\x01\xc8\xfd\xbc\xfd\xe0\x9e\xa5i\x9a\x11_\x12\xed\x91 \b\x80\xf8 A\xf8l\xb5Z\x9d\x89B>\xa0\xb1R\xab+\x10\x85\xc4\xef\x0e\x15\xfde\xd7O\xffj\xd7R_\xeeߟ=I\x95^\xc1ui\x9dο\xa1եI\xf0#n\xa5\x92Nju\x96\xa3\x13\xa9p\xe2\xea\f@(\xa5\x9d\xa0ϖ\xfe\x04H\xb4rFg\x19\x9a\xd5#\xaa\xf5S\xb9\xc1M)\xb3\x14\r\x03\xaf\xa6\xde\xff\xb4\xfe\x97\xf5Og\x00\x89A\x1e~/s\xb4N\xe4\xc5\x15\xa82\xcb\xce\x00\x94\xc8\xf1\nl\xb2ô\xccЮ\xf7\x98\xa1\xd1k\xa9\xcfl\x81\t\xcd\xf6htY\\A\xf3\x83\x1f\x140\xf1\xab\xb8\v\xe3\xf9S&\xad\xfb\xa5\xf3\xf9\x93\xb4\x8e\u007f*\xb2҈\xac5\x1f\u007f\xb5R=\x96\x990\xcd\xf73\x00\x9b\xe8\x02\xaf\xe03MU\x88\x04\xd33\x80\xb00\x9ez\x05\"M\x99T\"\xfbj\xa4rh\xaeuV\xe6\x15\x89V\x90\xa2M\x8c,\x1c\x93\xe2\xce\tWZ\xd0[p;l\xcfC\xed7\xab\xd5W\xe1vW\xb0\xb6\xdco]섭~\xf5$\xf2\x00\xc2'w ܬ3R=\x0e\xcd\xf6\x01\xae\x8dV\x80\xdf\v\x83\x96P\x86\x949\xab\x1e\xe1y\x87\n\x9c\x06S*F\xe5\xdfD\xf2T\x16\x03\x88\x14\x98\xac{x\x06L\xba\x1f\xe7p\xb9\xdf!d\xc2:p2G\x10aBx\x16\x96q\xd8j\x03n'\xedh\x1ao`%2؋\xac\xc4\v\x10*\x85\\\x1c\xc0 \xcd\x02\xa5j\xc1\xe3.v\r\xff\xa1\r\x82T[}\x05;\xe7\n{uy\xf9(]\xa5b\x13\x9d祒\xeep\xc9\xdaRnJ\xa7\x8d\xbdLq\x8f٥\x95\x8f+a\x92\x9dt\x98\xb8\xd2\xe0\xa5(\xe4\x8aQW\xacf\xd7y\xfa\x0f\x15G\xed\xbb\x0e\xaeG\xfb\xcd7V\x84\x13\x1c \x8d\xe8\x05\xc6\x0f\xf5\xabh\bM\x9f\x88:\xdfn\xee\xee\xdb\xc2$m\x9f\xfaL\xf7\x96\x845, \x82I\xb5Ű\xa3\xb7F\xe7\f\x13UZh\xa9\x1c\xff\x91d\x12U\x9f\xfc\xb6\xdc\xe4\xd2\x11\xdf\u007f/\xd1:\xe2\xd5\x1a\xae\xd9\xee\x90\x1c\x96\x05\xed\xc0t\r\xb7\n\xaeE\x8eٵ\xb0\xf8\xea\f J\xdb\x15\x116\x8e\x05m\x93\xd9\xef\xec\xa9\xd6\xfa\xa12o#\xfc\xaa\xf6\xf8]\x81Ig\xcb\xd08\xb9\x95\to\f֞\xb5\n\xe8iP߆w-\xff\xc2j\xaa\xff\xb5\x87\x87\xd7eլh\xc9~\xb8\x1ds\xb81c$W\x1e\x1a\xe9\x14\xa5\xfb\xdc\x1d҂-J\x04(3\x98t\xb5^\xac};\x82\tAխGp<\xe2*\xff\x84yAjc\x06\xc5\xfbЍP$\xfa\xa4\xb5;U\x19\xfeJ\xcd\xea\xa0]\xe1H\xb9\xf1t;$\xbe\xede\x1a\xb4\xd7\x11Wa\x92\xb3\xd4\x12+\xef\x94(\xecN;\xb2q\xbatC\xbdz\v\xb8\xbe\xbb\xed\rjq\x9e\xb0b\x1bΌv\x1a\x9e\x85<\xe6\xb4o$\x97\xd7w\xb7\xf0@.\x11V0\xc1[rp\xa5Q\xac\x8e\xbf\xa1H\x0f\xf7\xfaW\x8b\x90\x96\xac\x95*\xbb|1\x02x\x83[\xda\xf4\x06\t\x06\r@ch\x0fXFM\x97n\xcd\x0eG\x8a[Qf.(9i\xe1\xfdO\x90KU:<\xe6;L\xf3\xde\x13\x89\xc1\xf9\xd5\xd8{\xfd\xb3\xf5\x8c\x8c \xe9Ǒ\xa1\x03[\xaa\xd0)\xec\xb9\xdf\x18Ue\x86`\x0f\xd6a\x0e\x9b\x00\xa5\xb6\xd5\xcc\x15\xd6\aY\x16\xc0X\xd8\x1c*܇\xd7M^\xb8\xd8dx\x05Δ\xc3\xd3Nm\xdd!\xda|C\xebd\x12A\x99\xf3>i\xfc\xc8\x01\xc2\x18\xfea\x84(=\n\x90\x91\x17O\xe4h\x06\n\x91\xb7\x90e-\xe2\xceS\x05\xe0\xbf\x14|$\x03\x97\x90ٹ\n\xe6Lb\xc6&TiȴzD\xe3g$W\xe1Yf\x19oi\xcc\xf5\xbe\xe3d\xb5\x1b\xd9\x16\x83\x19\x19Iؖdv\xd6@\xb2?*#RY\x87\"]\x9f\xbf\x16\xf3\xf0{\x92\x95)\xa6u\x983\xa8Kz\x8c\xbb9\x1a\xc4\x01\xa1\x90\x8a43\x85_DtU\xff:B\r\xf65\x85A2\x18 \x95\x87I\xa4!E\xb3\x19Q\xd2Ԥ\xc3|\x04\xcfٝ\xbc\x80j\xc2\x18q\x98\xa0Y\x154/!Y=&\xb8b\x99L\x90\x88U;\\L5&\xcd\xc8\xfa\xfe\x1f\x12l\xa7\xf5S\f\x91\xfe\x9d\xfa5\x8e%$|6\x01\x1b܉\xbd\xd4\xc6\xf6\xa3\x13\xfc\x8eI\xe9Fw\x9bp\x90\xca\xed\x16\r\xc1›\x8e\xbf\xa7\x885mV\xa9\x99i\xc6\x1f\xad\xaba:1\x8f\xa91\xb6\x14v_F\xa1\x02#NV\x8fuC*\xf72-E\xc6jB\xa8įO\xd4\xf8\x8d)\xb7\x19\x818\xc2\xdf+\xa3j\x15ĥ\x8eW\xaa\x15\x92ۗk3f\xb7|;\x063N\x86\x8d`gr̅k\x9a)3\xb4\x01\x15o\xfe\x1a\xbds\xd1p\xca\at\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02\xdfb\xf5\xe7\be\a4i\xd7ߚU\xa2M#\x87l'\x93\x9d7V$e\f\vR\x8d\x965\x86(\x8a\xec0\xb5h\x88\x91\x8c0ٜ\xd2hZ\x84\xfa\xe8\xc3\x1dS$M\x8b\xd4\xc1M\x9b\xd1\xc6]\xaa\xd7b\xf3F\xf4\x0e\x9aꇄ\xfd\xf6h\xf8\xcb\v;\x91[\xa2]\xc3\xed\x160/\xdc\xe1\x02\xa4\xab\xbe\xc6@%W\xb1\xc1\xe3oƸ\xd3v\xcbm\u007f\xf4\x8b\xef\x96\x17\xe1Z\x8d\xc6߄il\xac\ue0adZİO\xed\x91\x17 \xb75\xc3\xd2\v\x8a!\x1d\xb2/5\x87h\xcbљ\xe5\xdcK\x12(\xd6\xf6R˅Kv7\xf51PĈ\x1e\xad\xfa\x00\xbc_^\xc50̃\b\x90P;\x15|\x82)\r\xe6\xfed\xf4\x9e\xf7G\xf3\x85=\xc0\x0f\x9f?b:G2\x88\x97ԣE}\xe8y:m\x14x\x81Q [\x8bb7\xad\x8e\xf1\xfc\xf9\xf7\x05\bx\u0083\xf7\xac\x06\x83ˡF\xac\x155H\x83|\x18\xcfj\xe4\t\x0f\f*\x9c\xaeG\xc1[\"*\xbe=\xe1!\xb6k\x8f\xa8\x84_8\xd7\xf3ԥ\x0f\xbc\x8a\x98\xadԴ\x9a\xa8a\xef\x80\xd3q\x8b\x85eJ\xa9j\x15\xc5O\\vͰΕ\xd2\x13\x1e\xdeY\xcf>\xda5;Y,\xa0\x00)l\xb0\xc8;\xac\xbaKy\x10\x99L\xeb\xc9x\x9f,\x80x\xab.\xe0\xb3v\xf4\x9f\x9b\xef\xd2\x12\x8a*\x85\x8f\x1a\xedg\xed\xf8˫\x92\xd8/\xe2D\x02\xfb\xc1\xbc-\x957\vD\x97E\xf378\xb0\t%\x11\xad\xd9&-\xdc*\x8a\xcf<}\x96\xb0i\x87\x15r\x1e\xad\xbc\xb4|\x1b\xa3\xb4Z\xb1\x99\xaef[\x00\xb4\x8dW`\x956\x1dN],\x848\x88b@\uf7ac\x95\xff\xe5\xe8\x1ek\xaa\x19,2\x91`Z\x9dJ\xf3\xa5\x99p\xf8(\x13\xc8\xd1<\"\x14d7\xe2\x85j\x81&\xf7\xed\x04)\x8cw-\xaa\x16\xcc\xc2\xc0\x1d\xd0P[Ѯ\x8f\xecY\xb19\xaa\xfb\xc8\r\xd9t\xf7\xb8U\xb2yg\u007f(\x8a\xfa픎e\x96e!\xbf\x8e}\x10\x8f\xa4w?r\xc1\xc7\xd6\u007f\x90ye\xf1\xfe3\xce\x1a\ni\xec\x1a>pBK\x86\xed\xf1\xd5)ak\xaa(\x90\x84\x89\xb4@r\xb2\x17\x19\xb9\x0f\xa4\xbc\x15`\xe6\x9d\t\xbd=\xf2\xa0\xe2T\xcc\xf3N[o\xf3\xebc\xf5\xf3'<\x9c_\x1ci\xaf\xf3[u\x1e\a\x93t\xfe\x91Ҫ\xbd\x16\xad\xb2\x03\x9c\xf3o\xe7\xec\x98-\xd9\"'8o\v\xa4:\xba+\xa7\x8e,\t\x05(֮\xbc\x16\x1a\\'X\x90\v?\xb7\x8ah\x99.\xb4\x1d\xb9]\x1cA뫶\xce\x1f\x00v\xdc\xed\x81\x13\u0098\xe8/\x9c\x1a\x82\xd8:4`\x9d6U2\x03\xa9\xdd\xde\x019q\xde\xce\xf3\x9eX]\x9fFz\xc0\x14d\x9e7\x1a\xc2\xeb\xf4s\x9f\xe5@\xff?\x0f3ag\x89a\x17F'h\xed\xbc(EZ\x8e\x99\x03\xdb\xfa\xb0V\xf8\xe0m\x1b\xa5\x9ac\x8e\x92\xab\xb6\xcc\x15'Ҟ\x10\xd8\xdc|o\x9d;\x93\x1a\xa2\xbfcD\xf9\x14\x1c\x81\x13\x1d\xf3\\\xf4\x13k\xa2ѽ\xf6\xa3\xab\r\x18\x80\xf9\x80\xc9<\x96\xacT\x96\xf9\xcdA$\xffj\x8eG.\xd5-O\x04\xef_\xcdY\x81J\x95㩡\xccu5\xbeaH\xfd!6~\x85*=C\xf3]\x8d\xc1\x0eg\x8fo2\xe29\x05\xe4L+\xedڇ5a\xa6w\x16\xb6\xd2X\xd7 \xbc\x00\xaa\xb4|M\xfd\xba1\xa6\xba1\xe6\xe4\x10\xf3\x8b\x1f\xdd:V\xdc\xe9\xe7\x90Դ$\xb0\xae\x88\xbf\x13{\x04\xb9\x05\xe9\x00U\xa2K\xc5\a^\xa4.h\x9a\x05\x10=\x13\xbd1\x89\xb4\x99\xad\xc1\xaa\xcc\xe3\t\xb2b\xe9\x94j\xf6t\xac=\xe4g!\xe3N\xa7\xe04\xb6\xba\xa9ġ\xa1\xd6͆\n\x19D\xed\xec\xb5\\|\x97y\x99\x83ȉ-K\xe2ƭ\xcf=\xaaR\xdd<\xaf\x9f\x85t!\x83\xd8_\xac.Ӧ\x89\u038b\f\x1dVYE\x89VV\xa6X\xbb\x0f\x81\xff\x839ZcM\xc0VȬ4\vt\xf4b\xce,\x8dۂzz\xf9`,\x1e\x91\x15\x133\xf2\xd0}\x81\xd3\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-{ 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xb5}l\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3'\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe2\xa8|\xa8\xe3\\\xa7\xa9\xa5\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x87\xe1\x8a\x18U\x9b\xb7\xb5\xd9\xff\x85\x04\xfe袵\xe9\xb8\xc01Qɗ\xde\x10\xe2}\xe5\xf5\r\xb9\xd5\xe31\x9ew\xb6Op\xabG@\xden!/3'\x8b\xacU\x92\xc2\xed\xf0P?y\xffM\xf3\xd3\xcb́\xa1}\xf9V\v\xf0\x18\xc8n\x80 ,\xe8\a]?\xfc\xcc%\xfa\x1a\u007f\xd1]R\xec\x15|\xdc5\xcf\xfc\xf3\x95\xc8g+\x91\x97@1\xd8G>OY\xfe,%\x92\xce'\x06[\x93SG>?Y\x14n\x9d\x18pMB\x9czn2\x1drM\x1f\xa7\xf5\x9f\x99\x9c\xe0NDH\xd8l\x97\x1f\xbe\x11\xd0&E3{\xb9\xb2D4g\x85\xb2\x17\x13u\xe7\xefU\x1d\xa8\xaa{Q\xaf\xf6\xc5\xcd\x18wt\xfd\n>\x81_\xa4J=oH\b[\xfe\x05\x17\xc9\xe4w2\xb5\xe33.F\x8d\xb7ٻ4\xb2X\bR\xa3\x1c\x14\xf1\xe5\xb6]ÍHvu\xc7\x11\x88<\xf3NX\xd8j\x93\v\a\xe7\xf5m\xdce5\x92\xbe\x9c\xaf\x01~\xd6\xf5Eh\xab\xca\xcd\b\\+\xf3\";P\xf4\x03\xe7]@?&:\xa3\xe2gC\xc1\xbfP\xd1,\"\x02\xbe\xeb\x8e\x18*8\x19\n\xbb%\x99.\xd3z\x86\tv\vu\x80\xaf\x0f\xecMq\x11\xa8\xa4)\x96\x15|\xa5*\x12\xee\xd5\xd2\x1a\x019V7r\x11\xc9\xc6/\x87\xad\xd3F<\xe2'\xedK{\xc6Ь;\xa2S\xdd5\xe8\xaa*U$\xbc\xfd\x1a\x95d\xbf\xb6>\xc0&\x83\xec\xa8\xda a;\xa6\xc4f\xf6\xb9sY\xc4\xe2\xee\xef?\xf9\x059\x99\xe3\xfac\xe9/\xeaW\x850\x16\x89\xd2\xd5B\xfd\xa0\u0378\x9d\xdb\xe9g.\xd7\u05ee\xbf٪p\x8c\x9c\xb1\xc69\x01'\xadfߩpY\x91.F\xe4\x1f\x86G\xb6\x02\xd9\x16\x13\xa7\xae\xf6\xf5v\x14\x96\xb0V'\x92u\x11\x1f\tq\xa2\xd8땊\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9VmT{\xab\xc6\nlvH\xf8\xeb\xd1\xc0\xd1\xe2\x9aN\xb3\xfe\xebu\x1f\xb2{*\x10\xc8\xfab\xa4\xd5ٖ\xb4u\t\xdac\xd2\xcd\xec\xff\xf1\xbd?컮\x86\xab\xbe\xae\xeaB\xb4g\x11\x94\xf5\xc5Vcj\f\xfb\xaa\xac\x89(\\i\x82yMJ\xc3u\xf3\b\b\xfa\xb2r\xa7U\x19n\xaa\xb6\xcf\xf0\xb2\xa9\xe3\xdeD\xfb\xb3U\xe3\a\xf8W\xd7\t\x1e-\x9c뭫\xaf\xea\xbe\"\xf8\xa7\xb1sp\x1fp\x9d\xc1\xb9\x9a\xcaԧN\xf2\r\x84\xe6\x81U}»1ԇ\xb36W\xf0\x19\x9f\a\xbe\xde(Z\xc4\xf1\x9d\x9aO\xcdĔ\xcf\b\x86*\xacO.q_\x8f\xe2\xbc\xd8\x01m\xd1Us\xbd\uef44\x1b\xaeV[w\xf19\xb0Cl\xfdG\xb9\xf5\a8\t\xad韎z\x8c*\xaeI\xa55\xa6\xb0\x06\xb7\xd4\xd1G\x8bf\xcf\xe5a+!\t6\xbc\xfd\xa5\xdc4\xe5\"\xe1\x8f?Ϛ])\x92\x04\v\x17\x12\xbb\xda\xff\x9aŹ/\xf7Z\xfdc\x15\xfcg\xa2\x95w\xb4\xed\x15\xfc\xe7\u007f\x9fA0\xc0\x0fտHA\x1f\xff7\x00\x00\xff\xffX\x13X\x17\xfbc\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VO\x8f\xeb4\x10\xbf\xe7S\x8c\x1e\x87w!\xe9{\xe2\x00\xca\r\x15\x0e+`\xb5\xda>\xed\x05qp\x9di;\xacc\x9b\xf1\xb8K\xf9\xf4\xc8v\xb2m\x93\x94]\x90\xf0-\xf6\xfc\xf9\xcdo\xfed\xaa\xba\xae+\xe5\xe9\t9\x90\xb3-(O\xf8\xa7\xa0M_\xa1y\xfe.4\xe4V\xc7\xcf\xd53ٮ\x85u\f\xe2\xfaG\f.\xb2\xc6\x1fpG\x96\x84\x9c\xadz\x14\xd5)Qm\x05\xa0\xacu\xa2\xd2uH\x9f\x00\xdaYag\fr\xbdG\xdb<\xc7-n#\x99\x0e9\x1b\x1f]\x1f?5\xdf6\x9f*\x00͘տP\x8fAT\xef[\xb0ј\n\xc0\xaa\x1e[\b\xc8II\x94\xc4\xc0\xf8G\xc4 \xa19\xa2Av\r\xb9*x\xd4\xc9\xf1\x9e]\xf4-\x9c\x1f\x8a\xfe\x00\xaa\x04\xb4ɦ6\xd9\xd4c1\x95_\r\x05\xf9\xe9\x96\xc4\xcf4Hy\x13Y\x99e@Y \x1c\x1c\xcb\xfd\xd9i\r!py!\xbb\x8fF\xf1\xa2r\x05\x10\xb4\xf3\xd8B\xd6\xf5JcW\x01\fLe[\xf5\xc0\xc5\xf1s1\xa7\x0fث\xe2\x04\xc0y\xb4\xdf?\xdc=}\xb3\xb9\xba\x06\xe80h&/\x99\xef\x85Ȁ\x02(\x18P\x808PZc\b\xa0#3Z\x81\x82\x12\xc8\xee\x1c\xf79G\xaf\xa6\x01\xd4\xd6E\x019 d\x05\xd9*\x03Ge\"~\r\xcavЫ\x130&/\x10텽,\x12\x1a\xf8\xc51f2[8\x88\xf8ЮV{\x92\xb1\xeb\xb4\xeb\xfbhIN\xab\xdc@\xb4\x8d\xe28\xac:<\xa2Y\x05\xda\u05ca\xf5\x81\x04\xb5Dƕ\xf2Tg\xe86w^\xd3w_\xf1Ч\xe1\xe3\x15V9\xa5\xca\n\xc2d\xf7\x17\x0f\xb9!\xfe!\x03\xa9\x1dJ}\x14\xd5\x12ř\xe8t\x95\xd8y\xfcq\xf3\x05F\xd79\x19S\xf63\xefg\xc5pNA\"\x8c\xec\x0e\xb9$qǮ\xcf6\xd1vޑ-ե\r\xa1\x9d\xd2\x1f\xe2\xb6'\tc\xed\xa6\\5\xb0Σ\b\xb6\b\xd1wJ\xb0k\xe0\xce\xc2Z\xf5h\xd6*\xe0\xff\x9e\x80\xc4t\xa8\x13\xb1\xefK\xc1\xe5\x14\x9d\n\x17\xd6.\x1e\xc61w#_\vݽ\xf1\xa8S\x06\x13\x89I\x9bv\xa4s{\xc0\xce1\xa8%\x95\xe6]H\xb2ƿ\xc42L\x92\x82f2_R\u007f\xbe\x8dfy\x9c䗃\n8\xbd\x9c`zH2S\xff\x86v\xa8O\xda`1Q\xa6\t\xbe\r%\x1d\xb4\xb1\x9f\xfb\xac\xe1\x1e_\x16n\x1fإɚ\xe7\xfa\xf5\xb9Q\x1bP\xfe7{\xb2\xb3p\xa7\x91\x15\xa9\xfc\x0f\xbb\x1c\xd5\x17\x03z0\x04\x1c\xadM};\x9b\x90\x19\xc8t\x92\xcfdH\xb0_@\xb3\x88\xe7\xce\xee\\\xde\x04Tr\xac\xa4\xf4\x13\x0e\xc9\x1e\xfc\x14\\\v\x06o纜\xf9\xf0z\x17\xa1\xe5\xe4?\xe9\u007fSN\xe3\x86\x18\x17}\xd7\x19\xd5\xe2C\xf2\xb8\xc4\xf8r\u007f\r(\xa31jk\xb0\x05\xe18\xd7.\xba\x8aY\x9d\xa6U3\x96\xday\x9fz\xa3\x80f\n\xa9O^\x0ehou\x03\xbc\xa8锿\xf2\f\xdb\xd3-\xd5\xf5\xebr8o\xa9R\xba-\xa4\xd9]\v-p\xf6.R\x16\xb3WJzq\xf3\x98\x11\xb2\xb9\x94\x1dg\xc6Uk\x8c\x8b\xc8<\x86\x9b\x10\x16\x93=\xbb\xcc滋\xf0\x828V\xfb1\xe0\xf3\xe8M\x9b\x9a\x17\xec\xee\xa7+\xee\x87\x0fW\xbbj\xfe\xd4\xcevT6t\xf8\xf5\xb7\xaaX\xc5\xeei\\0\xd3\xe5\xdf\x01\x00\x00\xff\xff\xfb\xb1p\x12\x1b\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WO\x8f۶\x13\xbd\xfbS\f\x92\xc3^\"9\xc1\xef\xf0+t)\x82M\x0fA\xf3g\x11o\xf7R\xf4@\x93#\x8b]\x8aTgHm\xddO_\f)\xad\xbd\xb6\x9cl\x8aV\x17C\x149|\xf3\u07bc!\xbd\xaa\xaaj\xa5\x06{\x87\xc46\xf8\x06\xd4`\xf1ψ^\u07b8\xbe\xff\x81k\x1b\xd6\xe3\x9bս\xf5\xa6\x81\xeb\xc41\xf4_\x90C\"\x8dﰵ\xdeF\x1b\xfc\xaaǨ\x8c\x8a\xaaY\x01(\xefCT2\xcc\xf2\n\xa0\x83\x8f\x14\x9cC\xaav\xe8\xeb\xfb\xb4\xc5m\xb2\xce \xe5\xe0\xf3\xd6\xe3\xeb\xfa\xff\xf5\xeb\x15\x80&\xcc\xcbom\x8f\x1cU?4\xe0\x93s+\x00\xafzl`\f.\xf5\xc8^\r܅\xe8\x82.\x9b\xd5#:\xa4P۰\xe2\x01\xb5콣\x90\x86\x06\x0e\x1fJ\x88\tW\xc9\xe9.G\xdbL\xd1>L\xd1\xf2\x04g9\xfe\xfc\x95I\x1f,\xc7\xe8\xcdK\x9a,\xcaWO\xb0ƽT\x11G\xb2~w\xf4!\x1b\xe1+\n\x88\aJ!\x94\xa5%\x8b\x03\xd12$\xec|\xf9is\v\xf3\xd6Y\x8cS\xf63\uf1c5|\x90@\b\xb3\xbeE*\"\xb6\x14\xfa\x1c\x13\xbd\x19\x82\xf51\xbfhgџ\xd2\xcfi\xdb\xdb(\xba\xff\x91\x90\xa3hU\xc3u\xeeB\xb0EH\x83Q\x11M\r\xef=\\\xab\x1eݵb\xfc\xcf\x05\x10\xa6\xb9\x12b\x9f'\xc1q\x03=\x9d\\X;6\xd8\xd4\xde.\xe8\xb5\xec\xe4̀\xfa\x89\x81$\x8am\xed\xe4\xec6\xd0\t\xafj\xf6\xf9r\xbc\xfa\xc9\xf4e\x83C\xe9\xfe\xadݝ\x8e\x02(c\xf2١\xdc\xcdŵ_!l!\xef뼓\x14j\x1bH\x10\x8d\xd6 Us\x9e\x13\x92DS\xc2\x16\x9d\xe1\xfa,\xe4\x05\xces*\x84F4V\xee\x1c\xe8S$\x8f\x13\xf3᧬/\x94\x1f\x02\xe4ң~\xea\xb1>\xa27\xb9\xa9\x9f\xa1\t\xb9\x86\x19\r<\xd8\xd8\x15s\xb8\xe3C\xeay*\xc8s\x8f\xfb\xa5\xe1\x13\xec\xb7\x1d\xca\xcc\xd2N\x11\x185a\x14\x1c\x8cN\xcc+ά\x01>&\xce\xf6R\x8b\x11AZ\x845\xf3\xea{ܟ\x13\r\xdf\x12w:\xef\xbf\r\xf9J\xce\xc5\x190a\x8b\x84>.Z\\\xee\x1e\xe41bv\xb9\t\x9a\xc5\xe0\x1a\x87\xc8\xeb0\"\x8d\x16\x1f\xd6\x0f\x81\xee\xad\xdfUBxU\n\x81\xd7\xf9ް~\x99\u007f.\xa4|\xfb\xf9\xdd\xe7\x06\xde\x1a\x03!vH\xa2Z\x9b\xdc\\hG\xa7ݫ\xdcq_A\xb2\xe6ǫ\u007f\xc2K\x18\x8as\x9e\xc1\xcd&W\xff^N\xee\fJ(\xda\x14U\x02\x81\xf4M\x11\xbb\x9f\xd4,\xfda\xa9\x10gL\xdb\x10\x1c\xaa\xf3ғ\xeek\t\xcd9\xa4Jv\xf8\x1e\x9b\xcd\xce\xfd\x86\xc9n\xa6ibx\xc9j^6\x17B\xb9\x97\xe4[\x8a\xda\xe1%\xa3/p\xbc\x9cJ\xf5\xb8\xc1\xb3ZtT1\xf1\xf77\xe9\xbcl\x9a\xb9\x9d\x1a\xb5N$\x05=\xc5\\\xb8\xd0\xfc;\x8dz\xe8\x14/\xb8\xed\x19\xa8od\xe5,\x83\xb3-\xea\xbdvX\x02Bh\x17\xaa\xe9\xbb ˃>\xf5K\xa5\xf5vT֩\xadÅo\xbfxu\xf1\xebE\xf1\x17\xf5<\x1bd\xb9\xb5\x98\x06\"\xa5\x12{\xaa\xb2i䠾\xd2\xd2\\\xd0|:\xfd\xdb\xf1\xe2œ\u007f\x0e\xf9U\a_\xceDn\xe0\xd7\xdfV%*\x9a\xbb\xf9\xa2/\x83\u007f\a\x00\x00\xff\xff\xe4\xf3S\x85\xb2\r\x00\x00"), } diff --git a/pkg/apis/velero/v1/backup.go b/pkg/apis/velero/v1/backup.go index 232955a35d..f3b2725425 100644 --- a/pkg/apis/velero/v1/backup.go +++ b/pkg/apis/velero/v1/backup.go @@ -100,10 +100,18 @@ type BackupSpec struct { // DefaultVolumesToRestic specifies whether restic should be used to take a // backup of all pod volumes by default. + // + // Deprecated: this field is no longer used and will be removed entirely in future. Use DefaultVolumesToFsBackup instead. // +optional - // + nullable + // +nullable DefaultVolumesToRestic *bool `json:"defaultVolumesToRestic,omitempty"` + // DefaultVolumesToFsBackup specifies whether pod volume file system backup should be used + // for all volumes by default. + // +optional + // +nullable + DefaultVolumesToFsBackup *bool `json:"defaultVolumesToFsBackup,omitempty"` + // OrderedResources specifies the backup order of resources of specific Kind. // The map key is the Kind name and value is a list of resource names separated by commas. // Each resource name has format "namespace/resourcename". For cluster resources, simply use "resourcename". diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index adfb9c2526..8c488dd4f6 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -337,6 +337,11 @@ func (in *BackupSpec) DeepCopyInto(out *BackupSpec) { *out = new(bool) **out = **in } + if in.DefaultVolumesToFsBackup != nil { + in, out := &in.DefaultVolumesToFsBackup, &out.DefaultVolumesToFsBackup + *out = new(bool) + **out = **in + } if in.OrderedResources != nil { in, out := &in.OrderedResources, &out.OrderedResources *out = make(map[string]string, len(*in)) diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index 2843cb54ba..0ef13e27d3 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -71,15 +71,15 @@ type Backupper interface { // kubernetesBackupper implements Backupper. type kubernetesBackupper struct { - backupClient velerov1client.BackupsGetter - dynamicFactory client.DynamicFactory - discoveryHelper discovery.Helper - podCommandExecutor podexec.PodCommandExecutor - resticBackupperFactory podvolume.BackupperFactory - resticTimeout time.Duration - defaultVolumesToRestic bool - clientPageSize int - uploaderType string + backupClient velerov1client.BackupsGetter + dynamicFactory client.DynamicFactory + discoveryHelper discovery.Helper + podCommandExecutor podexec.PodCommandExecutor + resticBackupperFactory podvolume.BackupperFactory + resticTimeout time.Duration + defaultVolumesToFsBackup bool + clientPageSize int + uploaderType string } func (i *itemKey) String() string { @@ -104,20 +104,20 @@ func NewKubernetesBackupper( podCommandExecutor podexec.PodCommandExecutor, resticBackupperFactory podvolume.BackupperFactory, resticTimeout time.Duration, - defaultVolumesToRestic bool, + defaultVolumesToFsBackup bool, clientPageSize int, uploaderType string, ) (Backupper, error) { return &kubernetesBackupper{ - backupClient: backupClient, - discoveryHelper: discoveryHelper, - dynamicFactory: dynamicFactory, - podCommandExecutor: podCommandExecutor, - resticBackupperFactory: resticBackupperFactory, - resticTimeout: resticTimeout, - defaultVolumesToRestic: defaultVolumesToRestic, - clientPageSize: clientPageSize, - uploaderType: uploaderType, + backupClient: backupClient, + discoveryHelper: discoveryHelper, + dynamicFactory: dynamicFactory, + podCommandExecutor: podCommandExecutor, + resticBackupperFactory: resticBackupperFactory, + resticTimeout: resticTimeout, + defaultVolumesToFsBackup: defaultVolumesToFsBackup, + clientPageSize: clientPageSize, + uploaderType: uploaderType, }, nil } @@ -205,7 +205,7 @@ func (kb *kubernetesBackupper) BackupWithResolvers(log logrus.FieldLogger, backupRequest.ResourceIncludesExcludes = collections.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()) - log.Infof("Backing up all pod volumes using Restic: %t", boolptr.IsSetToTrue(backupRequest.Backup.Spec.DefaultVolumesToRestic)) + log.Infof("Backing up all volumes using pod volume backup: %t", boolptr.IsSetToTrue(backupRequest.Backup.Spec.DefaultVolumesToFsBackup)) var err error backupRequest.ResourceHooks, err = getResourceHooks(backupRequest.Spec.Hooks.Resources, kb.discoveryHelper) diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index e218d2636a..6c09af7f10 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -2806,7 +2806,7 @@ func newSnapshotLocation(ns, name, provider string) *velerov1.VolumeSnapshotLoca } func defaultBackup() *builder.BackupBuilder { - return builder.ForBackup(velerov1.DefaultNamespace, "backup-1").DefaultVolumesToRestic(false) + return builder.ForBackup(velerov1.DefaultNamespace, "backup-1").DefaultVolumesToFsBackup(false) } func toUnstructuredOrFail(t *testing.T, obj interface{}) map[string]interface{} { diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index df5d48cd26..b669dd79f6 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -146,10 +146,10 @@ func (ib *itemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstr // nil it on error since it's not valid pod = nil } else { - // Get the list of volumes to back up using restic from the pod's annotations. Remove from this list + // Get the list of volumes to back up using pod volume backup 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 podvolume.GetPodVolumesUsingRestic(pod, boolptr.IsSetToTrue(ib.backupRequest.Spec.DefaultVolumesToRestic)) { + for _, volume := range podvolume.GetVolumesByPod(pod, boolptr.IsSetToTrue(ib.backupRequest.Spec.DefaultVolumesToFsBackup)) { 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 73a994b465..f18ce49092 100644 --- a/pkg/builder/backup_builder.go +++ b/pkg/builder/backup_builder.go @@ -174,6 +174,12 @@ func (b *BackupBuilder) SnapshotVolumes(val bool) *BackupBuilder { return b } +// DefaultVolumesToFsBackup sets the Backup's "DefaultVolumesToFsBackup" flag. +func (b *BackupBuilder) DefaultVolumesToFsBackup(val bool) *BackupBuilder { + b.object.Spec.DefaultVolumesToFsBackup = &val + return b +} + // DefaultVolumesToRestic sets the Backup's "DefaultVolumesToRestic" flag. func (b *BackupBuilder) DefaultVolumesToRestic(val bool) *BackupBuilder { b.object.Spec.DefaultVolumesToRestic = &val diff --git a/pkg/cmd/cli/backup/create.go b/pkg/cmd/cli/backup/create.go index 0289524f51..52aeab3d52 100644 --- a/pkg/cmd/cli/backup/create.go +++ b/pkg/cmd/cli/backup/create.go @@ -82,23 +82,23 @@ func NewCreateCommand(f client.Factory, use string) *cobra.Command { } type CreateOptions struct { - Name string - TTL time.Duration - SnapshotVolumes flag.OptionalBool - DefaultVolumesToRestic flag.OptionalBool - IncludeNamespaces flag.StringArray - ExcludeNamespaces flag.StringArray - IncludeResources flag.StringArray - ExcludeResources flag.StringArray - Labels flag.Map - Selector flag.LabelSelector - IncludeClusterResources flag.OptionalBool - Wait bool - StorageLocation string - SnapshotLocations []string - FromSchedule string - OrderedResources string - CSISnapshotTimeout time.Duration + Name string + TTL time.Duration + SnapshotVolumes flag.OptionalBool + DefaultVolumesToFsBackup flag.OptionalBool + IncludeNamespaces flag.StringArray + ExcludeNamespaces flag.StringArray + IncludeResources flag.StringArray + ExcludeResources flag.StringArray + Labels flag.Map + Selector flag.LabelSelector + IncludeClusterResources flag.OptionalBool + Wait bool + StorageLocation string + SnapshotLocations []string + FromSchedule string + OrderedResources string + CSISnapshotTimeout time.Duration client veleroclient.Interface } @@ -132,7 +132,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.DefaultVolumesToRestic, "default-volumes-to-restic", "", "Use restic by default to backup all pod volumes") + f = flags.VarPF(&o.DefaultVolumesToFsBackup, "default-volumes-to-fs-backup", "", "Use pod volume file system backup by default for volumes") f.NoOptDefVal = "true" } @@ -350,8 +350,8 @@ func (o *CreateOptions) BuildBackup(namespace string) (*velerov1api.Backup, erro if o.IncludeClusterResources.Value != nil { backupBuilder.IncludeClusterResources(*o.IncludeClusterResources.Value) } - if o.DefaultVolumesToRestic.Value != nil { - backupBuilder.DefaultVolumesToRestic(*o.DefaultVolumesToRestic.Value) + if o.DefaultVolumesToFsBackup.Value != nil { + backupBuilder.DefaultVolumesToFsBackup(*o.DefaultVolumesToFsBackup.Value) } } diff --git a/pkg/cmd/cli/install/install.go b/pkg/cmd/cli/install/install.go index a908ff23f6..fe4bebea30 100644 --- a/pkg/cmd/cli/install/install.go +++ b/pkg/cmd/cli/install/install.go @@ -43,40 +43,40 @@ import ( // InstallOptions collects all the options for installing Velero into a Kubernetes cluster. type InstallOptions struct { - Namespace string - Image string - BucketName string - Prefix string - ProviderName string - PodAnnotations flag.Map - PodLabels flag.Map - ServiceAccountAnnotations flag.Map - VeleroPodCPURequest string - VeleroPodMemRequest string - VeleroPodCPULimit string - VeleroPodMemLimit string - ResticPodCPURequest string - ResticPodMemRequest string - ResticPodCPULimit string - ResticPodMemLimit string - RestoreOnly bool - SecretFile string - NoSecret bool - DryRun bool - BackupStorageConfig flag.Map - VolumeSnapshotConfig flag.Map - UseRestic bool - Wait bool - UseVolumeSnapshots bool - DefaultResticMaintenanceFrequency time.Duration - GarbageCollectionFrequency time.Duration - Plugins flag.StringArray - NoDefaultBackupLocation bool - CRDsOnly bool - CACertFile string - Features string - DefaultVolumesToRestic bool - UploaderType string + Namespace string + Image string + BucketName string + Prefix string + ProviderName string + PodAnnotations flag.Map + PodLabels flag.Map + ServiceAccountAnnotations flag.Map + VeleroPodCPURequest string + VeleroPodMemRequest string + VeleroPodCPULimit string + VeleroPodMemLimit string + ResticPodCPURequest string + ResticPodMemRequest string + ResticPodCPULimit string + ResticPodMemLimit string + RestoreOnly bool + SecretFile string + NoSecret bool + DryRun bool + BackupStorageConfig flag.Map + VolumeSnapshotConfig flag.Map + UseRestic bool + Wait bool + UseVolumeSnapshots bool + DefaultRepoMaintenanceFrequency time.Duration + GarbageCollectionFrequency time.Duration + Plugins flag.StringArray + NoDefaultBackupLocation bool + CRDsOnly bool + CACertFile string + Features string + DefaultVolumesToFsBackup bool + UploaderType string } // BindFlags adds command line values to the options struct. @@ -106,13 +106,13 @@ func (o *InstallOptions) BindFlags(flags *pflag.FlagSet) { flags.BoolVar(&o.DryRun, "dry-run", o.DryRun, "Generate resources, but don't send them to the cluster. Use with -o. Optional.") flags.BoolVar(&o.UseRestic, "use-restic", o.UseRestic, "Create restic daemonset. Optional.") flags.BoolVar(&o.Wait, "wait", o.Wait, "Wait for Velero deployment to be ready. Optional.") - flags.DurationVar(&o.DefaultResticMaintenanceFrequency, "default-restic-prune-frequency", o.DefaultResticMaintenanceFrequency, "How often 'restic prune' is run for restic repositories by default. Optional.") + flags.DurationVar(&o.DefaultRepoMaintenanceFrequency, "default-repo-maintain-frequency", o.DefaultRepoMaintenanceFrequency, "How often 'maintain' is run for backup repositories by default. Optional.") flags.DurationVar(&o.GarbageCollectionFrequency, "garbage-collection-frequency", o.GarbageCollectionFrequency, "How often the garbage collection runs for expired backups.(default 1h)") flags.Var(&o.Plugins, "plugins", "Plugin container images to install into the Velero Deployment") 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.") + flags.BoolVar(&o.DefaultVolumesToFsBackup, "default-volumes-to-fs-backup", o.DefaultVolumesToFsBackup, "Bool flag to configure Velero server to use pod volume file system backup by default for all volumes on all backups. Optional.") flags.StringVar(&o.UploaderType, "uploader-type", o.UploaderType, fmt.Sprintf("The type of uploader to transfer the data of pod volumes, the supported values are '%s', '%s'", uploader.ResticType, uploader.KopiaType)) } @@ -135,11 +135,11 @@ func NewInstallOptions() *InstallOptions { ResticPodCPULimit: install.DefaultResticPodCPULimit, ResticPodMemLimit: install.DefaultResticPodMemLimit, // Default to creating a VSL unless we're told otherwise - UseVolumeSnapshots: true, - NoDefaultBackupLocation: false, - CRDsOnly: false, - DefaultVolumesToRestic: false, - UploaderType: uploader.ResticType, + UseVolumeSnapshots: true, + NoDefaultBackupLocation: false, + CRDsOnly: false, + DefaultVolumesToFsBackup: false, + UploaderType: uploader.ResticType, } } @@ -177,30 +177,30 @@ func (o *InstallOptions) AsVeleroOptions() (*install.VeleroOptions, error) { } return &install.VeleroOptions{ - Namespace: o.Namespace, - Image: o.Image, - ProviderName: o.ProviderName, - Bucket: o.BucketName, - Prefix: o.Prefix, - PodAnnotations: o.PodAnnotations.Data(), - PodLabels: o.PodLabels.Data(), - ServiceAccountAnnotations: o.ServiceAccountAnnotations.Data(), - VeleroPodResources: veleroPodResources, - ResticPodResources: resticPodResources, - SecretData: secretData, - RestoreOnly: o.RestoreOnly, - UseRestic: o.UseRestic, - UseVolumeSnapshots: o.UseVolumeSnapshots, - BSLConfig: o.BackupStorageConfig.Data(), - VSLConfig: o.VolumeSnapshotConfig.Data(), - DefaultResticMaintenanceFrequency: o.DefaultResticMaintenanceFrequency, - GarbageCollectionFrequency: o.GarbageCollectionFrequency, - Plugins: o.Plugins, - NoDefaultBackupLocation: o.NoDefaultBackupLocation, - CACertData: caCertData, - Features: strings.Split(o.Features, ","), - DefaultVolumesToRestic: o.DefaultVolumesToRestic, - UploaderType: o.UploaderType, + Namespace: o.Namespace, + Image: o.Image, + ProviderName: o.ProviderName, + Bucket: o.BucketName, + Prefix: o.Prefix, + PodAnnotations: o.PodAnnotations.Data(), + PodLabels: o.PodLabels.Data(), + ServiceAccountAnnotations: o.ServiceAccountAnnotations.Data(), + VeleroPodResources: veleroPodResources, + ResticPodResources: resticPodResources, + SecretData: secretData, + RestoreOnly: o.RestoreOnly, + UseRestic: o.UseRestic, + UseVolumeSnapshots: o.UseVolumeSnapshots, + BSLConfig: o.BackupStorageConfig.Data(), + VSLConfig: o.VolumeSnapshotConfig.Data(), + DefaultRepoMaintenanceFrequency: o.DefaultRepoMaintenanceFrequency, + GarbageCollectionFrequency: o.GarbageCollectionFrequency, + Plugins: o.Plugins, + NoDefaultBackupLocation: o.NoDefaultBackupLocation, + CACertData: caCertData, + Features: strings.Split(o.Features, ","), + DefaultVolumesToFsBackup: o.DefaultVolumesToFsBackup, + UploaderType: o.UploaderType, }, nil } @@ -393,8 +393,8 @@ 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") + if o.DefaultVolumesToFsBackup && !o.UseRestic { + return errors.New("--use-restic is required when using --default-volumes-to-fs-backup") } switch { @@ -404,8 +404,8 @@ func (o *InstallOptions) Validate(c *cobra.Command, args []string, f client.Fact return errors.New("Cannot use both --secret-file and --no-secret") } - if o.DefaultResticMaintenanceFrequency < 0 { - return errors.New("--default-restic-prune-frequency must be non-negative") + if o.DefaultRepoMaintenanceFrequency < 0 { + return errors.New("--default-repo-maintain-frequency must be non-negative") } if o.GarbageCollectionFrequency < 0 { diff --git a/pkg/cmd/cli/schedule/create.go b/pkg/cmd/cli/schedule/create.go index 00d84721c4..abd819d3bf 100644 --- a/pkg/cmd/cli/schedule/create.go +++ b/pkg/cmd/cli/schedule/create.go @@ -135,19 +135,19 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { }, Spec: api.ScheduleSpec{ Template: api.BackupSpec{ - IncludedNamespaces: o.BackupOptions.IncludeNamespaces, - ExcludedNamespaces: o.BackupOptions.ExcludeNamespaces, - IncludedResources: o.BackupOptions.IncludeResources, - ExcludedResources: o.BackupOptions.ExcludeResources, - IncludeClusterResources: o.BackupOptions.IncludeClusterResources.Value, - LabelSelector: o.BackupOptions.Selector.LabelSelector, - SnapshotVolumes: o.BackupOptions.SnapshotVolumes.Value, - TTL: metav1.Duration{Duration: o.BackupOptions.TTL}, - StorageLocation: o.BackupOptions.StorageLocation, - VolumeSnapshotLocations: o.BackupOptions.SnapshotLocations, - DefaultVolumesToRestic: o.BackupOptions.DefaultVolumesToRestic.Value, - OrderedResources: orders, - CSISnapshotTimeout: metav1.Duration{Duration: o.BackupOptions.CSISnapshotTimeout}, + IncludedNamespaces: o.BackupOptions.IncludeNamespaces, + ExcludedNamespaces: o.BackupOptions.ExcludeNamespaces, + IncludedResources: o.BackupOptions.IncludeResources, + ExcludedResources: o.BackupOptions.ExcludeResources, + IncludeClusterResources: o.BackupOptions.IncludeClusterResources.Value, + LabelSelector: o.BackupOptions.Selector.LabelSelector, + SnapshotVolumes: o.BackupOptions.SnapshotVolumes.Value, + TTL: metav1.Duration{Duration: o.BackupOptions.TTL}, + StorageLocation: o.BackupOptions.StorageLocation, + VolumeSnapshotLocations: o.BackupOptions.SnapshotLocations, + DefaultVolumesToFsBackup: o.BackupOptions.DefaultVolumesToFsBackup.Value, + OrderedResources: orders, + CSISnapshotTimeout: metav1.Duration{Duration: o.BackupOptions.CSISnapshotTimeout}, }, Schedule: o.Schedule, UseOwnerReferencesInBackup: &o.UseOwnerReferencesInBackup, diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 3d09f72132..c4cb302519 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -71,7 +71,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" "github.com/vmware-tanzu/velero/pkg/podexec" - "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/restore" "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/logging" @@ -133,7 +132,7 @@ type serverConfig struct { formatFlag *logging.FormatFlag repoMaintenanceFrequency time.Duration garbageCollectionFrequency time.Duration - defaultVolumesToRestic bool + defaultVolumesToFsBackup bool uploaderType string } @@ -163,7 +162,7 @@ func NewCommand(f client.Factory) *cobra.Command { profilerAddress: defaultProfilerAddress, resourceTerminatingTimeout: defaultResourceTerminatingTimeout, formatFlag: logging.NewFormatFlag(), - defaultVolumesToRestic: restic.DefaultVolumesToRestic, + defaultVolumesToFsBackup: podvolume.DefaultVolumesToFsBackup, uploaderType: uploader.ResticType, } ) @@ -214,7 +213,7 @@ func NewCommand(f client.Factory) *cobra.Command { command.Flags().StringVar(&config.pluginDir, "plugin-dir", config.pluginDir, "Directory containing Velero plugins") command.Flags().StringVar(&config.metricsAddress, "metrics-address", config.metricsAddress, "The address to expose prometheus metrics") command.Flags().DurationVar(&config.backupSyncPeriod, "backup-sync-period", config.backupSyncPeriod, "How often to ensure all Velero backups in object storage exist as Backup API objects in the cluster. This is the default sync period if none is explicitly specified for a backup storage location.") - command.Flags().DurationVar(&config.podVolumeOperationTimeout, "restic-timeout", config.podVolumeOperationTimeout, "How long backups/restores of pod volumes should be allowed to run before timing out.") + command.Flags().DurationVar(&config.podVolumeOperationTimeout, "fs-backup-timeout", config.podVolumeOperationTimeout, "How long pod volume file system backups/restores should be allowed to run before timing out.") command.Flags().BoolVar(&config.restoreOnly, "restore-only", config.restoreOnly, "Run in a mode where only restores are allowed; backups, schedules, and garbage-collection are all disabled. DEPRECATED: this flag will be removed in v2.0. Use read-only backup storage locations instead.") command.Flags().StringSliceVar(&config.disabledControllers, "disable-controllers", config.disabledControllers, fmt.Sprintf("List of controllers to disable on startup. Valid values are %s", strings.Join(controller.DisableableControllers, ","))) command.Flags().StringSliceVar(&config.restoreResourcePriorities, "restore-resource-priorities", config.restoreResourcePriorities, "Desired order of resource restores; any resource not in the list will be restored alphabetically after the prioritized resources.") @@ -227,9 +226,9 @@ func NewCommand(f client.Factory) *cobra.Command { command.Flags().StringVar(&config.profilerAddress, "profiler-address", config.profilerAddress, "The address to expose the pprof profiler.") 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.repoMaintenanceFrequency, "default-restic-prune-frequency", config.repoMaintenanceFrequency, "How often 'prune' is run for backup repositories by default.") + command.Flags().DurationVar(&config.repoMaintenanceFrequency, "default-repo-maintain-frequency", config.repoMaintenanceFrequency, "How often 'maintain' is run for backup repositories by default.") command.Flags().DurationVar(&config.garbageCollectionFrequency, "garbage-collection-frequency", config.garbageCollectionFrequency, "How often garbage collection is run for expired backups.") - command.Flags().BoolVar(&config.defaultVolumesToRestic, "default-volumes-to-restic", config.defaultVolumesToRestic, "Backup all volumes with restic by default.") + command.Flags().BoolVar(&config.defaultVolumesToFsBackup, "default-volumes-to-fs-backup", config.defaultVolumesToFsBackup, "Backup all volumes with pod volume file system backup by default.") command.Flags().StringVar(&config.uploaderType, "uploader-type", config.uploaderType, "Type of uploader to handle the transfer of data of pod volumes") return command @@ -622,7 +621,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.kubeClient.CoreV1(), s.kubeClient.CoreV1(), s.sharedInformerFactory.Velero().V1().BackupRepositories().Informer().HasSynced, s.logger), s.config.podVolumeOperationTimeout, - s.config.defaultVolumesToRestic, + s.config.defaultVolumesToFsBackup, s.config.clientPageSize, s.config.uploaderType, ) @@ -639,7 +638,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string backupTracker, s.mgr.GetClient(), s.config.defaultBackupLocation, - s.config.defaultVolumesToRestic, + s.config.defaultVolumesToFsBackup, s.config.defaultBackupTTL, s.config.defaultCSISnapshotTimeout, s.sharedInformerFactory.Velero().V1().VolumeSnapshotLocations().Lister(), diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 25879d060e..c7e0ca944c 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -86,7 +86,7 @@ type backupController struct { newPluginManager func(logrus.FieldLogger) clientmgmt.Manager backupTracker BackupTracker defaultBackupLocation string - defaultVolumesToRestic bool + defaultVolumesToFsBackup bool defaultBackupTTL time.Duration defaultCSISnapshotTimeout time.Duration snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister @@ -112,7 +112,7 @@ func NewBackupController( backupTracker BackupTracker, kbClient kbclient.Client, defaultBackupLocation string, - defaultVolumesToRestic bool, + defaultVolumesToFsBackup bool, defaultBackupTTL time.Duration, defaultCSISnapshotTimeout time.Duration, volumeSnapshotLocationLister velerov1listers.VolumeSnapshotLocationLister, @@ -138,7 +138,7 @@ func NewBackupController( backupTracker: backupTracker, kbClient: kbClient, defaultBackupLocation: defaultBackupLocation, - defaultVolumesToRestic: defaultVolumesToRestic, + defaultVolumesToFsBackup: defaultVolumesToFsBackup, defaultBackupTTL: defaultBackupTTL, defaultCSISnapshotTimeout: defaultCSISnapshotTimeout, snapshotLocationLister: volumeSnapshotLocationLister, @@ -263,7 +263,7 @@ func (c *backupController) processBackup(key string) error { } log.Debug("Preparing backup request") - request := c.prepareBackupRequest(original) + request := c.prepareBackupRequest(original, log) if len(request.Status.ValidationErrors) > 0 { request.Status.Phase = velerov1api.BackupPhaseFailedValidation } else { @@ -349,7 +349,7 @@ func patchBackup(original, updated *velerov1api.Backup, client velerov1client.Ba return res, nil } -func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkgbackup.Request { +func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup, logger logrus.FieldLogger) *pkgbackup.Request { request := &pkgbackup.Request{ Backup: backup.DeepCopy(), // don't modify items in the cache } @@ -373,8 +373,15 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg // calculate expiration request.Status.Expiration = &metav1.Time{Time: c.clock.Now().Add(request.Spec.TTL.Duration)} - if request.Spec.DefaultVolumesToRestic == nil { - request.Spec.DefaultVolumesToRestic = &c.defaultVolumesToRestic + // TODO: post v1.10. Remove this code block after DefaultVolumesToRestic is removed from CRD + // For now, for CRs created by old versions, we need to respect the DefaultVolumesToRestic value if it is set true + if boolptr.IsSetToTrue(request.Spec.DefaultVolumesToRestic) { + logger.Warn("DefaultVolumesToRestic field will be deprecated, use DefaultVolumesToFsBackup instead. Automatically remap it to DefaultVolumesToFsBackup") + request.Spec.DefaultVolumesToFsBackup = request.Spec.DefaultVolumesToRestic + } + + if request.Spec.DefaultVolumesToFsBackup == nil { + request.Spec.DefaultVolumesToFsBackup = &c.defaultVolumesToFsBackup } // find which storage location to use diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 20e340df06..84aafd7647 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -280,7 +280,7 @@ func TestBackupLocationLabel(t *testing.T) { formatFlag: formatFlag, } - res := c.prepareBackupRequest(test.backup) + res := c.prepareBackupRequest(test.backup, logger) assert.NotNil(t, res) assert.Equal(t, test.expectedBackupLocation, res.Labels[velerov1api.StorageLocationLabel]) }) @@ -342,7 +342,7 @@ func TestDefaultBackupTTL(t *testing.T) { formatFlag: formatFlag, } - res := c.prepareBackupRequest(test.backup) + res := c.prepareBackupRequest(test.backup, logger) assert.NotNil(t, res) assert.Equal(t, test.expectedTTL, res.Spec.TTL) assert.Equal(t, test.expectedExpiration, *res.Status.Expiration) @@ -350,6 +350,121 @@ func TestDefaultBackupTTL(t *testing.T) { } } +func TestDefaultVolumesToResticDeprecation(t *testing.T) { + tests := []struct { + name string + backup *velerov1api.Backup + globalVal bool + expectGlobal bool + expectRemap bool + expectVal bool + }{ + { + name: "DefaultVolumesToRestic is not set, DefaultVolumesToFsBackup is not set", + backup: defaultBackup().Result(), + globalVal: true, + expectGlobal: true, + expectVal: true, + }, + { + name: "DefaultVolumesToRestic is not set, DefaultVolumesToFsBackup is set to false", + backup: defaultBackup().DefaultVolumesToFsBackup(false).Result(), + globalVal: true, + expectVal: false, + }, + { + name: "DefaultVolumesToRestic is not set, DefaultVolumesToFsBackup is set to true", + backup: defaultBackup().DefaultVolumesToFsBackup(true).Result(), + globalVal: false, + expectVal: true, + }, + { + name: "DefaultVolumesToRestic is set to false, DefaultVolumesToFsBackup is not set", + backup: defaultBackup().DefaultVolumesToRestic(false).Result(), + globalVal: false, + expectGlobal: true, + expectVal: false, + }, + { + name: "DefaultVolumesToRestic is set to false, DefaultVolumesToFsBackup is set to true", + backup: defaultBackup().DefaultVolumesToRestic(false).DefaultVolumesToFsBackup(true).Result(), + globalVal: false, + expectVal: true, + }, + { + name: "DefaultVolumesToRestic is set to false, DefaultVolumesToFsBackup is set to false", + backup: defaultBackup().DefaultVolumesToRestic(false).DefaultVolumesToFsBackup(false).Result(), + globalVal: true, + expectVal: false, + }, + { + name: "DefaultVolumesToRestic is set to true, DefaultVolumesToFsBackup is not set", + backup: defaultBackup().DefaultVolumesToRestic(true).Result(), + globalVal: false, + expectRemap: true, + expectVal: true, + }, + { + name: "DefaultVolumesToRestic is set to true, DefaultVolumesToFsBackup is set to false", + backup: defaultBackup().DefaultVolumesToRestic(true).DefaultVolumesToFsBackup(false).Result(), + globalVal: false, + expectRemap: true, + expectVal: true, + }, + { + name: "DefaultVolumesToRestic is set to true, DefaultVolumesToFsBackup is set to true", + backup: defaultBackup().DefaultVolumesToRestic(true).DefaultVolumesToFsBackup(true).Result(), + globalVal: false, + expectRemap: true, + expectVal: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + formatFlag := logging.FormatText + + var ( + clientset = fake.NewSimpleClientset(test.backup) + sharedInformers = informers.NewSharedInformerFactory(clientset, 0) + logger = logging.DefaultLogger(logrus.DebugLevel, formatFlag) + fakeClient = velerotest.NewFakeControllerRuntimeClient(t) + ) + + apiServer := velerotest.NewAPIServer(t) + discoveryHelper, err := discovery.NewHelper(apiServer.DiscoveryClient, logger) + require.NoError(t, err) + + c := &backupController{ + genericController: newGenericController("backup-test", logger), + discoveryHelper: discoveryHelper, + client: clientset.VeleroV1(), + lister: sharedInformers.Velero().V1().Backups().Lister(), + kbClient: fakeClient, + snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), + clock: &clock.RealClock{}, + formatFlag: formatFlag, + defaultVolumesToFsBackup: test.globalVal, + } + + res := c.prepareBackupRequest(test.backup, logger) + assert.NotNil(t, res) + assert.NotNil(t, res.Spec.DefaultVolumesToFsBackup) + if test.expectRemap { + assert.Equal(t, res.Spec.DefaultVolumesToRestic, res.Spec.DefaultVolumesToFsBackup) + } else if test.expectGlobal { + assert.False(t, res.Spec.DefaultVolumesToRestic == res.Spec.DefaultVolumesToFsBackup) + assert.Equal(t, &c.defaultVolumesToFsBackup, res.Spec.DefaultVolumesToFsBackup) + } else { + assert.False(t, res.Spec.DefaultVolumesToRestic == res.Spec.DefaultVolumesToFsBackup) + assert.False(t, &c.defaultVolumesToFsBackup == res.Spec.DefaultVolumesToFsBackup) + } + + assert.Equal(t, test.expectVal, *res.Spec.DefaultVolumesToFsBackup) + }) + } +} + func TestProcessBackupCompletions(t *testing.T) { defaultBackupLocation := builder.ForBackupStorageLocation("velero", "loc-1").Default(true).Bucket("store-1").Result() @@ -359,20 +474,20 @@ func TestProcessBackupCompletions(t *testing.T) { timestamp := metav1.NewTime(now) tests := []struct { - name string - backup *velerov1api.Backup - backupLocation *velerov1api.BackupStorageLocation - defaultVolumesToRestic bool - expectedResult *velerov1api.Backup - backupExists bool - existenceCheckError error + name string + backup *velerov1api.Backup + backupLocation *velerov1api.BackupStorageLocation + defaultVolumesToFsBackup bool + expectedResult *velerov1api.Backup + backupExists bool + existenceCheckError error }{ // Completed { - name: "backup with no backup location gets the default", - backup: defaultBackup().Result(), - backupLocation: defaultBackupLocation, - defaultVolumesToRestic: true, + name: "backup with no backup location gets the default", + backup: defaultBackup().Result(), + backupLocation: defaultBackupLocation, + defaultVolumesToFsBackup: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -391,8 +506,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.True(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -405,10 +520,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(), - defaultVolumesToRestic: 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(), + defaultVolumesToFsBackup: false, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -427,8 +542,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: "alt-loc", - DefaultVolumesToRestic: boolptr.False(), + StorageLocation: "alt-loc", + DefaultVolumesToFsBackup: boolptr.False(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -447,7 +562,7 @@ func TestProcessBackupCompletions(t *testing.T) { Bucket("store-1"). AccessMode(velerov1api.BackupStorageLocationAccessModeReadWrite). Result(), - defaultVolumesToRestic: true, + defaultVolumesToFsBackup: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -466,8 +581,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: "read-write", - DefaultVolumesToRestic: boolptr.True(), + StorageLocation: "read-write", + DefaultVolumesToFsBackup: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -480,10 +595,10 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup with a TTL has expiration set", - backup: defaultBackup().TTL(10 * time.Minute).Result(), - backupLocation: defaultBackupLocation, - defaultVolumesToRestic: false, + name: "backup with a TTL has expiration set", + backup: defaultBackup().TTL(10 * time.Minute).Result(), + backupLocation: defaultBackupLocation, + defaultVolumesToFsBackup: false, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -502,9 +617,9 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - TTL: metav1.Duration{Duration: 10 * time.Minute}, - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.False(), + TTL: metav1.Duration{Duration: 10 * time.Minute}, + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.False(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -517,11 +632,11 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup without an existing backup will succeed", - backupExists: false, - backup: defaultBackup().Result(), - backupLocation: defaultBackupLocation, - defaultVolumesToRestic: true, + name: "backup without an existing backup will succeed", + backupExists: false, + backup: defaultBackup().Result(), + backupLocation: defaultBackupLocation, + defaultVolumesToFsBackup: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -540,8 +655,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.True(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -554,12 +669,12 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup specifying a false value for 'DefaultVolumesToRestic' keeps it", + name: "backup specifying a false value for 'DefaultVolumesToFsBackup' keeps it", backupExists: false, - backup: defaultBackup().DefaultVolumesToRestic(false).Result(), + backup: defaultBackup().DefaultVolumesToFsBackup(false).Result(), backupLocation: defaultBackupLocation, // value set in the controller is different from that specified in the backup - defaultVolumesToRestic: true, + defaultVolumesToFsBackup: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -578,8 +693,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.False(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.False(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -592,12 +707,12 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup specifying a true value for 'DefaultVolumesToRestic' keeps it", + name: "backup specifying a true value for 'DefaultVolumesToFsBackup' keeps it", backupExists: false, - backup: defaultBackup().DefaultVolumesToRestic(true).Result(), + backup: defaultBackup().DefaultVolumesToFsBackup(true).Result(), backupLocation: defaultBackupLocation, // value set in the controller is different from that specified in the backup - defaultVolumesToRestic: false, + defaultVolumesToFsBackup: false, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -616,8 +731,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.True(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -630,12 +745,12 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup specifying no value for 'DefaultVolumesToRestic' gets the default true value", + name: "backup specifying no value for 'DefaultVolumesToFsBackup' 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, + defaultVolumesToFsBackup: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -654,8 +769,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.True(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -668,12 +783,12 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, { - name: "backup specifying no value for 'DefaultVolumesToRestic' gets the default false value", + name: "backup specifying no value for 'DefaultVolumesToFsBackup' 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, + defaultVolumesToFsBackup: false, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -692,8 +807,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.False(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.False(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseCompleted, @@ -708,11 +823,11 @@ func TestProcessBackupCompletions(t *testing.T) { // Failed { - name: "backup with existing backup will fail", - backupExists: true, - backup: defaultBackup().Result(), - backupLocation: defaultBackupLocation, - defaultVolumesToRestic: true, + name: "backup with existing backup will fail", + backupExists: true, + backup: defaultBackup().Result(), + backupLocation: defaultBackupLocation, + defaultVolumesToFsBackup: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -731,8 +846,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.True(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseFailed, @@ -746,11 +861,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, - defaultVolumesToRestic: 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, + defaultVolumesToFsBackup: true, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -769,8 +884,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, }, Spec: velerov1api.BackupSpec{ - StorageLocation: defaultBackupLocation.Name, - DefaultVolumesToRestic: boolptr.True(), + StorageLocation: defaultBackupLocation.Name, + DefaultVolumesToFsBackup: boolptr.True(), }, Status: velerov1api.BackupStatus{ Phase: velerov1api.BackupPhaseFailed, @@ -823,21 +938,21 @@ func TestProcessBackupCompletions(t *testing.T) { require.NoError(t, err) c := &backupController{ - genericController: newGenericController("backup-test", logger), - discoveryHelper: discoveryHelper, - client: clientset.VeleroV1(), - lister: sharedInformers.Velero().V1().Backups().Lister(), - kbClient: fakeClient, - snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), - defaultBackupLocation: defaultBackupLocation.Name, - defaultVolumesToRestic: test.defaultVolumesToRestic, - backupTracker: NewBackupTracker(), - metrics: metrics.NewServerMetrics(), - clock: clock.NewFakeClock(now), - newPluginManager: func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, - backupStoreGetter: NewFakeSingleObjectBackupStoreGetter(backupStore), - backupper: backupper, - formatFlag: formatFlag, + genericController: newGenericController("backup-test", logger), + discoveryHelper: discoveryHelper, + client: clientset.VeleroV1(), + lister: sharedInformers.Velero().V1().Backups().Lister(), + kbClient: fakeClient, + snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(), + defaultBackupLocation: defaultBackupLocation.Name, + defaultVolumesToFsBackup: test.defaultVolumesToFsBackup, + backupTracker: NewBackupTracker(), + metrics: metrics.NewServerMetrics(), + clock: clock.NewFakeClock(now), + newPluginManager: func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, + backupStoreGetter: NewFakeSingleObjectBackupStoreGetter(backupStore), + backupper: backupper, + formatFlag: formatFlag, } pluginManager.On("GetBackupItemActions").Return(nil, nil) diff --git a/pkg/install/deployment.go b/pkg/install/deployment.go index cf34f175f1..d14e09e94e 100644 --- a/pkg/install/deployment.go +++ b/pkg/install/deployment.go @@ -32,19 +32,19 @@ import ( type podTemplateOption func(*podTemplateConfig) type podTemplateConfig struct { - image string - envVars []corev1.EnvVar - restoreOnly bool - annotations map[string]string - labels map[string]string - resources corev1.ResourceRequirements - withSecret bool - defaultResticMaintenanceFrequency time.Duration - garbageCollectionFrequency time.Duration - plugins []string - features []string - defaultVolumesToRestic bool - uploaderType string + image string + envVars []corev1.EnvVar + restoreOnly bool + annotations map[string]string + labels map[string]string + resources corev1.ResourceRequirements + withSecret bool + defaultRepoMaintenanceFrequency time.Duration + garbageCollectionFrequency time.Duration + plugins []string + features []string + defaultVolumesToFsBackup bool + uploaderType string } func WithImage(image string) podTemplateOption { @@ -99,9 +99,9 @@ func WithResources(resources corev1.ResourceRequirements) podTemplateOption { } } -func WithDefaultResticMaintenanceFrequency(val time.Duration) podTemplateOption { +func WithDefaultRepoMaintenanceFrequency(val time.Duration) podTemplateOption { return func(c *podTemplateConfig) { - c.defaultResticMaintenanceFrequency = val + c.defaultRepoMaintenanceFrequency = val } } @@ -129,9 +129,9 @@ func WithUploaderType(t string) podTemplateOption { } } -func WithDefaultVolumesToRestic() podTemplateOption { +func WithDefaultVolumesToFsBackup() podTemplateOption { return func(c *podTemplateConfig) { - c.defaultVolumesToRestic = true + c.defaultVolumesToFsBackup = true } } @@ -157,8 +157,8 @@ 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") + if c.defaultVolumesToFsBackup { + args = append(args, "--default-volumes-to-fs-backup=true") } if len(c.uploaderType) > 0 { @@ -288,8 +288,8 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1.Deployment deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, "--restore-only") } - if c.defaultResticMaintenanceFrequency > 0 { - deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, fmt.Sprintf("--default-restic-prune-frequency=%v", c.defaultResticMaintenanceFrequency)) + if c.defaultRepoMaintenanceFrequency > 0 { + deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, fmt.Sprintf("--default-repo-maintain-frequency=%v", c.defaultRepoMaintenanceFrequency)) } if c.garbageCollectionFrequency > 0 { diff --git a/pkg/install/deployment_test.go b/pkg/install/deployment_test.go index ef5f871a06..604af44dfd 100644 --- a/pkg/install/deployment_test.go +++ b/pkg/install/deployment_test.go @@ -46,9 +46,9 @@ func TestDeployment(t *testing.T) { assert.Equal(t, 7, len(deploy.Spec.Template.Spec.Containers[0].Env)) assert.Equal(t, 3, len(deploy.Spec.Template.Spec.Volumes)) - deploy = Deployment("velero", WithDefaultResticMaintenanceFrequency(24*time.Hour)) + deploy = Deployment("velero", WithDefaultRepoMaintenanceFrequency(24*time.Hour)) assert.Len(t, deploy.Spec.Template.Spec.Containers[0].Args, 2) - assert.Equal(t, "--default-restic-prune-frequency=24h0m0s", deploy.Spec.Template.Spec.Containers[0].Args[1]) + assert.Equal(t, "--default-repo-maintain-frequency=24h0m0s", deploy.Spec.Template.Spec.Containers[0].Args[1]) deploy = Deployment("velero", WithGarbageCollectionFrequency(24*time.Hour)) assert.Len(t, deploy.Spec.Template.Spec.Containers[0].Args, 2) diff --git a/pkg/install/resources.go b/pkg/install/resources.go index 7053b87c59..aa9b5f237e 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -209,30 +209,30 @@ func appendUnstructured(list *unstructured.UnstructuredList, obj runtime.Object) } type VeleroOptions struct { - Namespace string - Image string - ProviderName string - Bucket string - Prefix string - PodAnnotations map[string]string - PodLabels map[string]string - ServiceAccountAnnotations map[string]string - VeleroPodResources corev1.ResourceRequirements - ResticPodResources corev1.ResourceRequirements - SecretData []byte - RestoreOnly bool - UseRestic bool - UseVolumeSnapshots bool - BSLConfig map[string]string - VSLConfig map[string]string - DefaultResticMaintenanceFrequency time.Duration - GarbageCollectionFrequency time.Duration - Plugins []string - NoDefaultBackupLocation bool - CACertData []byte - Features []string - DefaultVolumesToRestic bool - UploaderType string + Namespace string + Image string + ProviderName string + Bucket string + Prefix string + PodAnnotations map[string]string + PodLabels map[string]string + ServiceAccountAnnotations map[string]string + VeleroPodResources corev1.ResourceRequirements + ResticPodResources corev1.ResourceRequirements + SecretData []byte + RestoreOnly bool + UseRestic bool + UseVolumeSnapshots bool + BSLConfig map[string]string + VSLConfig map[string]string + DefaultRepoMaintenanceFrequency time.Duration + GarbageCollectionFrequency time.Duration + Plugins []string + NoDefaultBackupLocation bool + CACertData []byte + Features []string + DefaultVolumesToFsBackup bool + UploaderType string } func AllCRDs() *unstructured.UnstructuredList { @@ -272,7 +272,7 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { appendUnstructured(resources, bsl) } - // A snapshot location may not be desirable for users relying on restic + // A snapshot location may not be desirable for users relying on pod volume backup/restore if o.UseVolumeSnapshots { vsl := VolumeSnapshotLocation(o.Namespace, o.ProviderName, o.VSLConfig) appendUnstructured(resources, vsl) @@ -286,7 +286,7 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { WithImage(o.Image), WithResources(o.VeleroPodResources), WithSecret(secretPresent), - WithDefaultResticMaintenanceFrequency(o.DefaultResticMaintenanceFrequency), + WithDefaultRepoMaintenanceFrequency(o.DefaultRepoMaintenanceFrequency), WithGarbageCollectionFrequency(o.GarbageCollectionFrequency), WithUploaderType(o.UploaderType), } @@ -303,8 +303,8 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { deployOpts = append(deployOpts, WithPlugins(o.Plugins)) } - if o.DefaultVolumesToRestic { - deployOpts = append(deployOpts, WithDefaultVolumesToRestic()) + if o.DefaultVolumesToFsBackup { + deployOpts = append(deployOpts, WithDefaultVolumesToFsBackup()) } deploy := Deployment(o.Namespace, deployOpts...) diff --git a/pkg/podvolume/util.go b/pkg/podvolume/util.go index a7ab78245a..d75b393153 100644 --- a/pkg/podvolume/util.go +++ b/pkg/podvolume/util.go @@ -48,6 +48,10 @@ const ( // InitContainer is the name of the init container added // to workload pods to help with restores. InitContainer = "restic-wait" + + // DefaultVolumesToFsBackup specifies whether pod volume backup should be used, by default, to + // take backup of all pod volumes. + DefaultVolumesToFsBackup = false ) // volumeBackupInfo describes the backup info of a volume backed up by PodVolumeBackups @@ -253,9 +257,9 @@ func contains(list []string, k string) bool { return false } -// GetPodVolumesUsingRestic returns a list of volume names to backup for the provided pod. -func GetPodVolumesUsingRestic(pod *corev1api.Pod, defaultVolumesToRestic bool) []string { - if !defaultVolumesToRestic { +// GetVolumesByPod returns a list of volume names to backup for the provided pod. +func GetVolumesByPod(pod *corev1api.Pod, defaultVolumesToFsBackup bool) []string { + if !defaultVolumesToFsBackup { return GetVolumesToBackup(pod) } diff --git a/pkg/podvolume/util_test.go b/pkg/podvolume/util_test.go index 88b5746685..936ee26d37 100644 --- a/pkg/podvolume/util_test.go +++ b/pkg/podvolume/util_test.go @@ -348,16 +348,16 @@ func TestGetVolumesToBackup(t *testing.T) { } } -func TestGetPodVolumesUsingRestic(t *testing.T) { +func TestGetVolumesByPod(t *testing.T) { testCases := []struct { - name string - pod *corev1api.Pod - expected []string - defaultVolumesToRestic bool + name string + pod *corev1api.Pod + expected []string + defaultVolumesToFsBackup bool }{ { - name: "should get PVs from VolumesToBackupAnnotation when defaultVolumesToRestic is false", - defaultVolumesToRestic: false, + name: "should get PVs from VolumesToBackupAnnotation when defaultVolumesToFsBackup is false", + defaultVolumesToFsBackup: false, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -368,8 +368,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should get all pod volumes when defaultVolumesToRestic is true and no PVs are excluded", - defaultVolumesToRestic: true, + name: "should get all pod volumes when defaultVolumesToFsBackup is true and no PVs are excluded", + defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ Spec: corev1api.PodSpec{ Volumes: []corev1api.Volume{ @@ -381,8 +381,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should get all pod volumes except ones excluded when defaultVolumesToRestic is true", - defaultVolumesToRestic: true, + name: "should get all pod volumes except ones excluded when defaultVolumesToFsBackup is true", + defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -401,8 +401,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should exclude default service account token from restic backup", - defaultVolumesToRestic: true, + name: "should exclude default service account token from restic backup", + defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ Spec: corev1api.PodSpec{ Volumes: []corev1api.Volume{ @@ -416,8 +416,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should exclude host path volumes from restic backups", - defaultVolumesToRestic: true, + name: "should exclude host path volumes from restic backups", + defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -438,8 +438,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should exclude volumes mounting secrets", - defaultVolumesToRestic: true, + name: "should exclude volumes mounting secrets", + defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -460,8 +460,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should exclude volumes mounting config maps", - defaultVolumesToRestic: true, + name: "should exclude volumes mounting config maps", + defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -482,8 +482,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should exclude projected volumes", - defaultVolumesToRestic: true, + name: "should exclude projected volumes", + defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -517,8 +517,8 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { expected: []string{"resticPV1", "resticPV2", "resticPV3"}, }, { - name: "should exclude DownwardAPI volumes", - defaultVolumesToRestic: true, + name: "should exclude DownwardAPI volumes", + defaultVolumesToFsBackup: true, pod: &corev1api.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ @@ -553,7 +553,7 @@ func TestGetPodVolumesUsingRestic(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - actual := GetPodVolumesUsingRestic(tc.pod, tc.defaultVolumesToRestic) + actual := GetVolumesByPod(tc.pod, tc.defaultVolumesToFsBackup) sort.Strings(tc.expected) sort.Strings(actual) diff --git a/pkg/restic/common.go b/pkg/restic/common.go index 90a38f58be..f1ecb9a718 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -37,10 +37,6 @@ const ( // at which restic prune is run. DefaultMaintenanceFrequency = 7 * 24 * time.Hour - // DefaultVolumesToRestic specifies whether restic should be used, by default, to - // take backup of all pod volumes. - DefaultVolumesToRestic = false - // insecureSkipTLSVerifyKey is the flag in BackupStorageLocation's config // to indicate whether to skip TLS verify to setup insecure HTTPS connection. insecureSkipTLSVerifyKey = "insecureSkipTLSVerify" diff --git a/test/e2e/basic/namespace-mapping.go b/test/e2e/basic/namespace-mapping.go index 2f72f23440..3a8d56ccef 100644 --- a/test/e2e/basic/namespace-mapping.go +++ b/test/e2e/basic/namespace-mapping.go @@ -57,7 +57,7 @@ func (n *NamespaceMapping) StartRun() error { n.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", n.BackupName, "--include-namespaces", strings.Join(*n.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } n.RestoreArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "restore", n.RestoreName, diff --git a/test/e2e/basic/resources-check/namespaces.go b/test/e2e/basic/resources-check/namespaces.go index ae6e15455a..7c1d6ad4f6 100644 --- a/test/e2e/basic/resources-check/namespaces.go +++ b/test/e2e/basic/resources-check/namespaces.go @@ -81,7 +81,7 @@ func (m *MultiNSBackup) StartRun() error { m.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", m.BackupName, "--exclude-namespaces", strings.Join(*m.NSExcluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } m.RestoreArgs = []string{ diff --git a/test/e2e/basic/resources-check/namespaces_annotation.go b/test/e2e/basic/resources-check/namespaces_annotation.go index f3f602147c..5033dd14ec 100644 --- a/test/e2e/basic/resources-check/namespaces_annotation.go +++ b/test/e2e/basic/resources-check/namespaces_annotation.go @@ -56,7 +56,7 @@ func (n *NSAnnotationCase) Init() error { n.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", n.BackupName, "--include-namespaces", strings.Join(*n.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } n.RestoreArgs = []string{ diff --git a/test/e2e/basic/resources-check/rbac.go b/test/e2e/basic/resources-check/rbac.go index c07329d817..7657669cfb 100644 --- a/test/e2e/basic/resources-check/rbac.go +++ b/test/e2e/basic/resources-check/rbac.go @@ -71,7 +71,7 @@ func (r *RBACCase) Init() error { r.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", r.BackupName, "--include-namespaces", strings.Join(*r.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } r.RestoreArgs = []string{ diff --git a/test/e2e/orderedresources/ordered_resources.go b/test/e2e/orderedresources/ordered_resources.go index af6074ee8f..1691413f9a 100644 --- a/test/e2e/orderedresources/ordered_resources.go +++ b/test/e2e/orderedresources/ordered_resources.go @@ -127,7 +127,7 @@ func (o *OrderedResources) Init() error { } o.ScheduleArgs = []string{"--schedule", "@every 1m", - "--include-namespaces", o.Namespace, "--default-volumes-to-restic", "--ordered-resources"} + "--include-namespaces", o.Namespace, "--default-volumes-to-fs-backup", "--ordered-resources"} var orderStr string for kind, resource := range o.OrderMap { orderStr += fmt.Sprintf("%s=%s;", kind, resource) diff --git a/test/e2e/resource-filtering/base.go b/test/e2e/resource-filtering/base.go index fcfd1fc37c..08def1a7bd 100644 --- a/test/e2e/resource-filtering/base.go +++ b/test/e2e/resource-filtering/base.go @@ -53,7 +53,7 @@ func (f *FilteringCase) Init() error { f.NamespacesTotal = 3 f.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", f.BackupName, - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } f.RestoreArgs = []string{ diff --git a/test/e2e/resource-filtering/exclude_label.go b/test/e2e/resource-filtering/exclude_label.go index 2147221c93..b5e46621bd 100644 --- a/test/e2e/resource-filtering/exclude_label.go +++ b/test/e2e/resource-filtering/exclude_label.go @@ -65,7 +65,7 @@ func (e *ExcludeFromBackup) Init() error { e.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", e.BackupName, "--include-namespaces", e.NSBaseName, - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } e.RestoreArgs = []string{ diff --git a/test/e2e/resource-filtering/exclude_namespaces.go b/test/e2e/resource-filtering/exclude_namespaces.go index b76b15fb19..8d195c2476 100644 --- a/test/e2e/resource-filtering/exclude_namespaces.go +++ b/test/e2e/resource-filtering/exclude_namespaces.go @@ -83,7 +83,7 @@ func (e *ExcludeNamespaces) Init() error { "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", e.BackupName, "--exclude-namespaces", strings.Join(*e.nsExcluded, ","), "--include-namespaces", strings.Join(*e.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } e.RestoreArgs = []string{ @@ -96,7 +96,7 @@ func (e *ExcludeNamespaces) Init() error { e.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", e.BackupName, "--include-namespaces", strings.Join(*e.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } e.RestoreArgs = []string{ diff --git a/test/e2e/resource-filtering/exclude_resources.go b/test/e2e/resource-filtering/exclude_resources.go index 1080ee70f1..844a4243e6 100644 --- a/test/e2e/resource-filtering/exclude_resources.go +++ b/test/e2e/resource-filtering/exclude_resources.go @@ -66,7 +66,7 @@ func (e *ExcludeResources) Init() error { "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", e.BackupName, "--include-namespaces", strings.Join(*e.NSIncluded, ","), "--exclude-resources", "secrets", - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } e.RestoreArgs = []string{ @@ -86,7 +86,7 @@ func (e *ExcludeResources) Init() error { e.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", e.BackupName, "--include-namespaces", strings.Join(*e.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } e.RestoreArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "restore", e.RestoreName, diff --git a/test/e2e/resource-filtering/include_namespaces.go b/test/e2e/resource-filtering/include_namespaces.go index 1d6fdf49d6..5783586fad 100644 --- a/test/e2e/resource-filtering/include_namespaces.go +++ b/test/e2e/resource-filtering/include_namespaces.go @@ -73,7 +73,7 @@ func (i *IncludeNamespaces) Init() error { i.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", i.BackupName, "--include-namespaces", strings.Join(*i.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } i.RestoreArgs = []string{ @@ -92,7 +92,7 @@ func (i *IncludeNamespaces) Init() error { i.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", i.BackupName, "--include-namespaces", strings.Join(*i.allTestNamespaces, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } i.RestoreArgs = []string{ diff --git a/test/e2e/resource-filtering/include_resources.go b/test/e2e/resource-filtering/include_resources.go index 102991bc66..268ee6fb85 100644 --- a/test/e2e/resource-filtering/include_resources.go +++ b/test/e2e/resource-filtering/include_resources.go @@ -63,7 +63,7 @@ func (i *IncludeResources) Init() error { i.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", i.BackupName, "--include-resources", "deployments,configmaps", - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } i.RestoreArgs = []string{ @@ -81,7 +81,7 @@ func (i *IncludeResources) Init() error { i.BackupArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", i.BackupName, "--include-namespaces", strings.Join(*i.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } i.RestoreArgs = []string{ "create", "--namespace", VeleroCfg.VeleroNamespace, "restore", i.RestoreName, diff --git a/test/e2e/resource-filtering/label_selector.go b/test/e2e/resource-filtering/label_selector.go index 3b0edfd3d4..fff56b1b56 100644 --- a/test/e2e/resource-filtering/label_selector.go +++ b/test/e2e/resource-filtering/label_selector.go @@ -64,7 +64,7 @@ func (l *LabelSelector) Init() error { "create", "--namespace", VeleroCfg.VeleroNamespace, "backup", l.BackupName, "--selector", "resourcefiltering=true", "--include-namespaces", strings.Join(*l.NSIncluded, ","), - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } l.RestoreArgs = []string{ diff --git a/test/e2e/util/velero/velero_utils.go b/test/e2e/util/velero/velero_utils.go index e7bc0404e4..36ff519ca9 100644 --- a/test/e2e/util/velero/velero_utils.go +++ b/test/e2e/util/velero/velero_utils.go @@ -323,10 +323,10 @@ func VeleroBackupNamespace(ctx context.Context, veleroCLI, veleroNamespace strin if backupCfg.UseVolumeSnapshots { args = append(args, "--snapshot-volumes") } else { - args = append(args, "--default-volumes-to-restic") + args = append(args, "--default-volumes-to-fs-backup") // To workaround https://github.com/vmware-tanzu/velero-plugin-for-vsphere/issues/347 for vsphere plugin v1.1.1 // if the "--snapshot-volumes=false" isn't specified explicitly, the vSphere plugin will always take snapshots - // for the volumes even though the "--default-volumes-to-restic" is specified + // for the volumes even though the "--default-volumes-to-fs-backup" is specified // TODO This can be removed if the logic of vSphere plugin bump up to 1.3 args = append(args, "--snapshot-volumes=false") } @@ -362,7 +362,7 @@ func VeleroBackupExcludeNamespaces(ctx context.Context, veleroCLI string, velero args := []string{ "--namespace", veleroNamespace, "create", "backup", backupName, "--exclude-namespaces", namespaces, - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } return VeleroBackupExec(ctx, veleroCLI, veleroNamespace, backupName, args) } @@ -373,7 +373,7 @@ func VeleroBackupIncludeNamespaces(ctx context.Context, veleroCLI string, velero args := []string{ "--namespace", veleroNamespace, "create", "backup", backupName, "--include-namespaces", namespaces, - "--default-volumes-to-restic", "--wait", + "--default-volumes-to-fs-backup", "--wait", } return VeleroBackupExec(ctx, veleroCLI, veleroNamespace, backupName, args) }