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..fa4e981885 100644 --- a/config/crd/v1/bases/velero.io_backups.yaml +++ b/config/crd/v1/bases/velero.io_backups.yaml @@ -42,9 +42,16 @@ 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 v2.0." + 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..45cd203561 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 v2.0." + 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..6225e89caa 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\xfc\xb0\xfe\xe7\xf5\x0f\x17\x00\x85F7\xfc\x91Wh,\xab\xea\x1b\x90\x8d\x10\x17\x00\x92Ux\x03\x1bV<5\xb5Y\x1fP\xa0Vk\xae.L\x8d\x05͵Ӫ\xa9o\xa0{ᇄu\xf8=\xfcɍv\x0f\x047\xf6\xe7\xde\xc3_\xb8\xb1\xeeE-\x1a\xcdD;\x93{f\xb8\xdc5\x82\xe9\xf8\xf4\x02\xc0\x14\xaa\xc6\x1b\xf8DSԬ\xc0\xf2\x02 l\xc7M\xb9\n\v>\xfc\xe8!\x14{\xac\x98_\v\x80\xaaQ\xbe\xbf\xbf\xfb\xf2O\x0f\x83\xc7\x00%\x9aB\xf3\xda:\xa4\xf8\x85\x017\xc0\xe0\x8b\xdb\x16\xe8\x80~\xb0{fAc\xadѠ\xb4\x06\xec\x1e\xa1`\xb5m4\x82\xda\xc2\xcf\xcd\x06\xb5D\x8b\xa6\x05\rP\x88\xc6X\xd4`,\xb3\b\xcc\x02\x83Zqi\x81K\xb0\xbcB\xf8\xc3\xfb\xfb;P\x9b߰\xb0\x06\x98,\x81\x19\xa3\n\xce,\x96pP\xa2\xa9Џ\xfd㺅ZkU\xa3\xb6<\xe2ٷ\x1eW\xf5\x9e\x8e\xb6\xf7\x8e0\xe0{AI\xec\x84~\x1b\x01\x8bX\x06\xa4\xd1~잛n\xbb\x8eC\x06\x80\x81:1\x19\x16\xbf\x86\a\xd4\x04\x06\xcc^5\xa2$.<\xa0&\x84\x15j'\xf9\u007f\xb7\xb0\rX\xe5&\x15\xccb`\x80\xaeqiQK&\xe0\xc0D\x83W\x0e%\x15;\x82F\x9a\x05\x1aك纘5\xfc\x9b\xd2\b\\n\xd5\r쭭\xcd\xcd\xf5\xf5\x8e\xdbx\x9a\nUU\x8d\xe4\xf6x\xed\x0e\x06\xdf4Vis]\xe2\x01ŵ\xe1\xbb\x15\xd3Ş[,\x88\x90\u05ec\xe6+\xb7t\xe9NԺ*\xff!2\x80y7X\xab=\x123\x1a\xab\xb9\xdc\xf5^8\xae\x9f\xa1\x00\x1d\x00\xcf_~\xa8\xdfE\x87hzD\xd8\xf9\xfc\xf1\xe1\xb1\xcf{܌\xb1\xef\xf0\xdecȎ\x04\x840.\xb7\xa8=\x11\xb7ZU\x0e&\xca\xd2s\x9fc]\xc1Q\x8e\xd1o\x9aM\xc5-\xd1\xfd/\r\x1abr\xb5\x86['b`\x83\xd0\xd4%q\xe6\x1a\xee$ܲ\n\xc5-3\xf8\xe6\x04 L\x9b\x15!6\x8f\x04}\xe98\xee\xec\xb1\xd6{\x11e\xd9\x04\xbd\xbc@x\xa8\xb1\x18\x1c\x18\x1aŷ\xbcp\xc7\x02\xb6Jw\xf2\u008b\xab\xf5\x00d\xfa\xc8R+\f\u007f\x90\xac6{eI\xfe\xaaƎ{\x8c\x16t\xfbp7\x1a\x10\x17\x13\x96\xe6\xc4Jc\xb0\xa4s\xf6̸\xa5\xe5\x9d\xc0\x04\x02\x04_\x9c\x84\x89\xf0\x9c\xa4i\f\xd8FKwJ?#+\x8f\x8f\xea\xcf\x06\xa1l\x1c\xb3F]q\x05\x1b\xdc*\x8d\t\xb8\x1ai\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\u007fJ\xf8@\x92\xab yr\xbaZ\b\x92\x8b\xa3p\xd2R*\x10J\xeeP\xfb\xd9H+:B\x12\xa7\xba\x9dO-\x9d\x84\xca\xf8\\\xc5F\v%\x9dAƷ,\xf9\x81\x97\r\x13\xc0\xa5\xb1L\x16~?\xac]\xd7\xe9~`\x8e\xc8'k\xf6\"?\xae\x9c(1P\xf0J\"(\r\x15Y5\xa7]\xc7vXצ\xb6\xbda$\xb3\x94gQ\xdd\b4a*\xaf$:\x19p5\t\xba\xa5\x88\xb7\x88\x05۠\x00\x83\x02\v\xabt\x1a\x1dKD\xf6-G\xaeM`1!ᆖH\xb7\xb1\x19\x90\xe0̔=/\xf6^\xe4\x13\a98P*4\ue533\xba\x16ǩM\xc2\x12\xe5\xc3$s\a\xbdk\vG~\f/u\xf8\xbb\x96!\x1b\xbb\xb6 %\x87\x98m\xd9\x01\xac\x9a\xdd\xf6\xffO\xc4F\xb1\xff\x02\xa6\xbd;\x19\xfa\xbaLK(\xe5\xe4\x94\xdem\x01\xab\xda\x1e\xaf\x80\xdb\xf8t\t\"\x19L\xdd\xfc\u007fDŽ9\x9f\xe3\xef\xc6#_\x95\xe3g\xa9\xb2\x04\x91\xa8\xd2N\xffwH\x14\xa7,\x1e\x82\xae\xc8&\xc8/\xfdQW\xc0\xb7-A\xca+\xf2~,\xea\x11e\xbe鼼\x062r\xf4\x1d\xb5\x8a\xd9b\xff\xf1+Y^\xa6\v\x9bf\xe2e<\xd8ۯў\x1f*\xe6\x05\xb8\xe0\xc2,\\c\xe5\xc37\x8f\x0e\x9b\xdd\x13gQ\xbd\xff\xf4\x01\xcb9\xf4@\x1e\xe7\x9dl\xe4\xfdh\xb1\xfd\xa9\x83Q\x9e\xbb\x8d`\xfa\xb4\xfe\x8d\x0f\xcc]\x01\x83'\xac\x95I\xdc\tM,\xe5^\x19\xeb#\x8b\x03\xb3\xf4\x9c(\x16x\x1e\n\xd1+`[\u007f\u007f\xadt\xbc\x99$\xb17\n\xb8\x12\xd5̼\x84%2\xb6\x111\x0f\x94\x1c\xab\xcb\xee\x04{yz\xe9\xaf+\xdd$\xacp\xc6\xc5\"\xdcZ\xab\x02\x8d\x99g\x91\fi\xbd\x10$l\x03\x84\xcc;0\xfe\xdao>(\x19[\xbeAJH:Ӕ\xff\xf8\xb5\x17\xbd\xa4\xc3O\u007f/1߹\xeb\x02wf\xab\x8a\x8dﷳ\x96x\xebG\xc6c\x12\x00y\xd7@\xef\x1aw\xd4\xf3-\xc8\xc0H\xbf\a5]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\xea9f\tL^\xa8\xa6\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?1.ނlv*\x99\"\xd5\x06d\x8bY\x15\xfdt\x8f\x8a}\xe5US\x01\xab\b\xf5\xb9n\xcf\xd6\xe7b\f(\xdefd8\xb8N\x8dXE\x87\xaa\x16hsO\xa4Ͻ\xa0cbx\x89\xadb\x0e\\\xa0$0\xd82.\x1a\x9d)!\xcf\xc2\xed9\xbeF\x10\x16\xaf\xe7D\xe4M\xber\xa8\xc8\b\xc4f\x1a\x8b\xf3Һ\xd6\xf9\xa6\xe2\xbd\xc6<\xf3l)(\x1dͳZs\xe2%\xf5\xda\x16Z`1&\x8f\xdfM\xb4\x93\xf6\xddD[h\xdfM\xb4\xc9\xf6\xddD[n\xdfM\xb4о\x9bh\xb1}7Ѿ\x9bhs\xdd\xe6\xa4\xf5Ҋ\xfcw#\x13/\x17W\x91q==\xb7\xc4\x19\xf8!\x9b\xe2\xd6\u007fC\x92\x9bay\x97\x1e\x95\xc8\xed\r\x1f\xa7\xac\xdcw5)\x0e\xe8\x92.:UҦ\\\xd2\x01\x89\xec\xed\xd3\xe0\x17\x920\xbf!\xfb6'\xe1g)\xcdg\x98gڦ\xd9\xc4DS\x15'I\xe0!~\x9fCfo?\x87d\x98\xaf\xe3\xecܸҿy\x0ejF*\xceB\x02\xce|b\xee\x1c\xbeF\xae\xc7\x10az\x900\xfa\xbb\xc1\xd7B\x96\xcctnL\xb8\tB\xcb\x0e?\xae\x87o\xac\n\x992\xf0\xcc\xed>\xb1\x95\xe7=Jw\x87%w\xfd\xb4\xd7\xc8o\xe1C\xa91\x1eAi\x90\\8t\xcep\xeb\x00\xbd\xf0k\xed]\xb8\xb3\xcf\xe5\xbc\xfb\x91\x97K\xf3\xe2\f\x9aa\x86̄\x88>\xf7\xca(?Q8?Gf>\xa9\xe5\x9c̘q\xde\xcb$\xd0\xe5|\x98\x1c\xcfq!\xf7\xe5\x05\x19/\x99َ\xdf|1\x96\x93\xd3\xf2\xa2L\x96ń\xc0\xcc\xfc\x95af\xca<\xc83\xb2V\xb2\x90\xb3\x9c\xa1rv^J\xc8\x03\x99\xddGv6J\"\xcfd\x16\xf0d\x0e\xca\\v\xc9BT\xea4\xf3$?\xa7d\x16\xb4\xcb7Y\xce$y\xbd|\xd1װ\x81\xa7E\xcdb6Ȣ\x8d<\xbf\xbe\xc5|\x8fs\xb2<\x161\xf6\u008c\x8e6ccb\xdes\xf38\x86y\x1a\x13@s\xb27&\xb23& \xce\xe6l\xe4\xe6dL\xc0^P\xbb\xb3\\2\xf32\xfd9/,\xea7\xf1\xd7⨗nL遹\xb8d\xa1\xff:\xeaN\xb4\x8cVӼ\xf9\x99\xb2<\xb9ݟo~V\x8d\xb0\xbc\x16.\x9c\u007f\xe0e\xd2i\xb4{<\xb6\x1fg\xfe\xa6\xdcgN\x9b\xa3\x83\xf4\xeb\xe7\x96=\xd7##\x9a\x19xF!\x80\xa5\x98\xebd\xe7\x85\xff\"\xbdP+$\x99O\a.|v\x1a>\\\xbf\xf2\x1c\xec\xbe\xe4JE<\xed\x1e+\x82\x12\xbf_=\xc3\xfd\x987\x10\xbd-\xeb\x9e\xfd\xa5A}\x04u@\xddY\f\v\xdf\x11\xf8\x83f\x1a\xd1%n\x05\xf9\xe1\xeb \x8c\f\xe7\xee\xc0\xc1{\xe9UX\x12\xech\x8d\x0e\x0e\x9dy\xd1Қ\xc4\x1b\xf9\x01\x13]Ӂ\x0fՎN\xbc_\xb2=s\x93\xf0\xdf\xd6u8\xdfyXT\xdbo\xe2@\xbc܅\x98\x01\x99\x9bT\x9fw\x01\xb5\x98D\xffV\xaeĒ3\x91mE\xe5%ɿEr\xfc\x19I\xf1g8\x15\xe7\xb9\x15\xd9h\xcaI~\u007f\x13\xe7\xe2\r\u074b\xb7p0^\xe6b,\x80\x1c%\xb5礫g]\xaef\xdf/\xe4\\\x8e._\x01̧\xa1g\xa4\x9fg\\\x0e,\xad4#\xcd\xfc\xbc\xf4\xf2\f\x1c\xbe\x91\xf3\xf1F\xee\xc7[8 o\xeb\x82,:!\x8b\x9c3\xfb\xfa\xc5\xd1e\xa5KԳ\xc1\xf8\\V\x9be\xb2\x91\xbf0\x9cs\xf4Em\xac\xd3B\xbd\x06\xa6i*\xa4\xdc~\xfdY\xc0\xcf\\\x96\x9e\x1e\xc4T==\xee*[\xb9\xfc\xf7֨\xe8\xec\xb34\xd0ѥ\x82\xc1\x9aiW\xfals\xf4\x17\x93f\r\x1fY\xb1\x1fv\x84=3\xb0U\xbaJ\x1aL\x97\xed\x8d\xccu\x1cEO.\xd7\x00?\xa9\xf6ҫ_Q\xc1\xf0\xaa\x16G\xf2\x03\xe0r8\xe4e\f\x90d\x1e\x13\x8a,\x85\x92;\v\xbe\xdeðw\xe2\xf2.\x16\xdc)\x84j\xca\x16\xfa\x04\xf1\x98<\xc2\xfd\x17g\x93\xb82!EW2%X\x1d\xd1\xe7\x1bWT\xf9\xd3\xeb_\xe6\x19\xab4\xdb\xe1/\xcaW\xcfZ\xc2İ\xf7\xa0tZ\x90\x15\xf1r=~{\x91ҡ\xa1\x8e\xd7\bX\x973sRÉV\x99\x12\"3\xe7\xcfZ\xb1\xb0\x99\xc7\xc7_\xfc\x06,\xafp\xfd\xa1\xf1\x17\xa7\xab\x9ai\x83\x84\u03781?hC\xffݫ\xe7TlC\x85=\xffi\xbcn\x8d./\xc7\xddϞ\xb5\xfaà\x16XD\xd1\x12\xa3~I\x8f\xea9f=\"\xf9S\x9etȧ\xe0\xf4\xca!\xba\x90\x85\xfb\xae\xe6u\xcb\xfcLI\xed\xa9\x82q\xaeH\xdar\xc98_K-\x14\x88\f\xd9]\x8dv5zB\x9d5W\xd3\xe6eU\xe3|2ʠh\xe7<\x9dnOG\xb8Ҍ\xba\xecU\x8dk\x8bw=3\xd3&\xbc$\x15i\aΏt\x96,A\xc3\x12\xf0\x80\x12\x94t\xf9-\xae\xfa\x8d/\x1f:\x1e\x93\x80ڇ\x12\x12h\x9aZ(V\xc6\x13\x1euV(9\xf9\xe8\xe4\x97>\xa0~gf`\xb6\xe5\xd8\x12H8\x15\x98^\xb1\xdc@\xc9,\xae\x92@\xb3d_\x92\xd9\nÇ\x8cn\xde[K~A\xcaV\x1e\x97\xfd\x9b\x1a\x19\xf5\xafU\x96\t\x90M\xb5\xf1\n\x9d\xc5\x0e)\xfa\x9d\x14\xff3!\xe3i\xe6x\xf9\x8dqiqw\x12S<\xdd\xd9m䟳w֎\x9cڙi\x8a\x02\x8d\xd96B\xa4L\xfb\x96s_\u007f\x9b.\x97o\xb1ƙ\xeb\xe4E\xa0K\x04\x8c\xc5\xf0|&`\x85ư],n\xf6L\x1ah\x87\x12\x9dᓊ7zǰ\xcb\x1c\x1b\x96\xf6\xf2\x11,V؆\x85\t\xe2\xcd\u007f\xaf\u05fb\x94] \xd4Η1䱈kT\xcdg\xe2\xe4k\xcdu\x8e*\xff\xd8v$ܸ\xe0\xb3#DWt\x17\x05\xdfq҃D\xa4\x1d\xd3\x1b\xb6\xc3U\xa1\x84@\x97f~\xba\xae\xb7<\xac!?\xef32\xb3\xb8\xb5\x9f\xfa}C\xa4\xc3S\xdbW\xc6`\xbeH\xa2\xab\xc1j\xb9Ʈ\xa8\xf1ɂ\x94\x9b\xf8,\xd5\xed\xb1\x90,\xff{\xba\xd2~\xdfx\xc0\x82\\\xf5pb5\xe0\xab`\f\xa6\xbdي\xfd\xa6\xf4\x15T\\\xd2?d\xf1\xbbPD\x1c|\xd6\xfa]ͺ\x85u\xdfS\x9f6M\xba\xa7H1\x1e\x88)S5\x9d\x1a\xbb\x82OxjY\xf9lW,]\xf0-U\xf3\x98\xba\xdc\xc9{\xadv\xe4\x0f'^\xb6\xc2+\xf1\xee\x9ei˙\x10G?\xc9\xe4\xec\x89\x17\x1f\x90\x14פ\xf5\x92FkX\xe5\x12fC\xb7\xce\xf5\xe6\xd2s\x82\xcbSݨ\xc6\x0eDI'\x8a\xd2a\u007f\al\r\x9f\x94\xc5\x18\xd1\xe5C\x98$|\xd1\xd8\x15n\xb7J[\xef\xe9\xafV\xc0\xb7\xc1\x1aJ\xc0\xa53\xe1n\xa4|\tbර\x94\xef\xb8\xd79:\xda\x1dBW\xe1\xa9bG\x9f\xb3Ȋ\x82\x8cm\xbc6\x96\x89\x84|\xfb\xa6\x1c(gv\x12\xf7a\xf9\xe7\x84\x1dv\x82\xf0\xbb~\xff\xf6\xc3\xf1V\xbb9p\x1es.\xa7\xdc\xcb\xf6\xa4\xa6\x03\x97i\x8c\x12\x9e5\xb7\x96\xe4i\xff\xca\x0e,IP!\xc0\x90L\x99(\x138'\xd9\xdd{ҽw\xd3!ġ\u007f\xd3v\x9eR\xddas\x8aȲq(\x98ؖ\xfff\x89\x9b8\x96HY\xec\x99\xdc\x11Si\xd5\xec\xf6\x91/'4\xe3T\x04\xae\xa1EA-\x9a\x1d\xb1z\xb8.\xb1\x8d\x96\xbd\x10L\xb8@){\xcbe\xc5\xd3\xe4JCH8\x96\xc1\xbf\x0e\x85\xffV[\xad\xaaU\xa0\x85\xbb\xe5\xb8\n\xa1\x11\xcd\x15\xd9\xff\xe4\xc8O\x00\xed*l96\xa8k\x94\xc0LXO\xc6\aU\xf3d\x9d\x8bSX\xa6m\xaeW\xf10\xe8\xbc\xe0P8\xc8\xe9\xf5>\x84\xc0\x8f\xff\xb0\xecv\xfc\x83\x04W`\xb8\x8c\x15\xf8}`ɳ\x82!?C\xa3\xf3Փ\x17X'\x1e\xc2\xc0\x1f\x18.\xff\xaf\xeb\n\x1cZ\r\xf31Ǧ\xfc2\xea>\xca\xceu\x85\xa6\xdb.\xc1\x0eL\xe0\xe3\x0f|\xeb\xef\xd4\nZ\xf5\x1f\xff\xe6Y\xb7\x87,\x9b\xe5ݬ\xb9\xe2,\x91\xd6\xeeX(+}/\x90\xec\b\x838\xb4\x84ޝe\xf2\x1e^\xe6Ľ\xa6\a\x17\u007f\x1c\xe2u\xfc\x9a\xc3\xcb|\xb77s\xdc^ww\xcf\xcc\x15\xa4_:c\xff\x1e\xba%<\xb7\x00!\xe1\xbb%\xb6\xd1zs\x8b\xbe[\xcfu\x8bk\x9c\xa8v=r\xe7^\xc9yKꁓ\x87N\x80\x96\xbd\xb3\x1df\nO\xba\x80\x18+\n$v\xfd4\xfe\x11\x98K_\x8b=\xfe\u038b\xfb\xb3Pҫ[s\x03\xff\xf1_\x17\x10\"\xae_\xe2\x0f\xba\xd0\xc3\xff\v\x00\x00\xff\xff\u007f\xa7\xf6\x180g\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\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=Ms\x1c\xb9nw\xfd\n\x94rpR\xa5\x19\xd9\xc9!)\xdd\x1cY[Q=\xc7vYz\xba$9p\xba1\x1a\xae\xba\xc9~${\xe4\xc9\xd6\xfe\xf7\x14@\xf6\xe7\xf4\a{,U\xf6m\x89\x97]\xf5\x90 \b\x80\xf8 A\xf8l\xb5Z\x9d\x89B>\xa0\xb1R\xab+\x10\x85\xc4\x1f\x0e\x15\xfde\xd7O\xfff\xd7R_\xee?\x9c=I\x95^\xc1ui\x9dο\xa3եI\xf0\x13n\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\u07bf_\xff\xeb\xfa\xfd\x19@b\x90\x87\xdf\xcb\x1c\xad\x13yq\x05\xaa̲3\x00%r\xbc\x02\x9b\xec0-3\xb4\xeb=fh\xf4Z\xea3[`B\xb3=\x1a]\x16W\xd0\xfc\xe0\a\x05L\xfc*\xee\xc2x\xfe\x94I\xeb\xfe\xd2\xf9\xfcYZ\xc7?\x15YiD֚\x8f\xbfZ\xa9\x1e\xcbL\x98\xe6\xfb\x19\x80Mt\x81W\xf0\x85\xa6*D\x82\xe9\x19@X\x18O\xbd\x02\x91\xa6L*\x91}3R94\xd7:+\xf3\x8aD+H\xd1&F\x16\x8eIq\xe7\x84+-\xe8-\xb8\x1d\xb6\xe7\xa1\xf6\xab\xd5\xea\x9bp\xbb+X[\xee\xb7.v\xc2V\xbfz\x12y\x00\xe1\x93;\x10n\xd6\x19\xa9\x1e\x87f\xfb\b\xd7F+\xc0\x1f\x85AK(CʜU\x8f\xf0\xbcC\x05N\x83)\x15\xa3\xf2\xef\"y*\x8b\x01D\nL\xd6=<\x03&ݏs\xb8\xdc\xef\x102a\x1d8\x99#\x880!<\v\xcb8l\xb5\x01\xb7\x93v\x9e&\x04\xa4\x83\xadG\xe7s\xff\xb3G(\x15\x0e\x03:-P\x95T\xaf\x8f$\xb2\x03\xf3\xe3#F\x00c\x12\x15\xa2\xb4,\x1c\xcd\xe8o\xedO\x1e\xc0F\xeb\f\x85:k:\xed?x\xd9Kv\x98\x8b\xab\xd0Y\x17\xa8>~\xbb}\xf8\x97\xbb\xceg\xe8\xc9R\xa0\x14H\v\x02\x1exc\x80\t[\x18\xdcN80H\x9cG\xe5\xa8GapUQ7\xadA\x02h\x03\x05\x1a\xa9S\x99T\\\xe1\xc1v\xa7\xcb,\x85\r\x12\x83\xd6\xf5\x80\xc2\xe8\x02\x8d\x93\xd5\xd6\xf3\xad\xa5jZ_{\x18\xbf\xa3E\xf9^^\x12Ѳ\xf0\x85\r\x85i\xa0\x83\xdf\x1f\xd26\xf83\x93:\x80\x81:\t\x05z\xf3+&n\rwh\bL\x85u\xa2\xd5\x1e\rQ яJ\xfeo\rے\xd4;\x16F\x87A\x1f4\x8d7\xb0\x12\x19\xecEV\xe2\x05\b\x95B.\x0e`\x90f\x81R\xb5\xe0q\x17\xbb\x86\xff\xd4\x06A\xaa\xad\xbe\x82\x9ds\x85\xbd\xba\xbc|\x94\xaeR\xb1\x89\xce\xf3RIw\xb8dm)7\xa5\xd3\xc6^\xa6\xb8\xc7\xec\xd2\xcaǕ0\xc9N:L\\i\xf0R\x14rŨ+V\xb3\xeb<\xfd\x87\x8a\xa3\xf6]\aף\xfd\xe6\x1b+\xc2\t\x0e\x90F\xf4\x02\xe3\x87\xfaU4\x84\xa6OD\x9d\xef7w\xf7ma\x92\xb6O}\xa6{K\xc2\x1a\x16\x10\xc1\xa4\xdab\xd8\xd1[\xa3s\x86\x89*-\xb4T\x8e\xffH2\x89\xaaO~[nr\xe9\x88\xef\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\xa3H\x0f\xf7\xfa\xaf\x16!-Y+Uv\xf9b\x04\xf0\x06\xb7\xb4\xe9\r\x12\f\x1a\x80\xc6\xd0\x1e\xb0\x8c\x9a.ݚ\x1d\x8e\x14\xb7\xa2\xcc\\Pr\xd2\u0087\xf7\x90KU:<\xe6;L\xf3\xde\x13\x89\xc1\xf9\xd5\xd8{\xfd\x8b\xf5\x8c\x8c 駑\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|G\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\x15|\"\x03\x97\x90ٹ\n\xe6Lb\xc6&TiȴzD\xe3g$W\xe1Yf\x19oi\xcc\xf5\xbe\xe3d\xb5\x1b\xd9\x16\x83\x19\x19I\xd8\xff\xf3\xfa\xfd\xfa\xfc\xb5\x98\x82?\x92\xacL1\xad×A\x1d\xd1c\xc8\xcd\xd1 \x0e\xf4\x84T\xa4q)\xac\"b\xaa\xfaבU\xb2\x0f)\f\x92!\x00\xa9Q\xe37\xa6\xb4f\x04\xe2\b\u007fo\x88\xaaU\x10\x97:ަVH\xee\\\xae͘=\xf2\xed\x18\xcc8\x196\x82\x9d\xc41\u05eci\xa6\xcc\xd0\x06T\xbcYk\xf4\xceE\xc3)\x1f\xa8eb\x83\x19X\xcc0qڌ\x93'F\b|\x8b՟#\x94\x1dФ]?jV\x896\x8d\x1c\xad\x9dLv\xde\b\x91\x941,H5Z\xd6\x18\xa2(\xb2\xc3Ԣ!F2\xc2dsJ\xa3i\x11\xea\xa3\x0fwL\x914-R\a7mF\x1bw\xa9^\x8b\xcd\x1b\xd1;h\xaa\x9f\x12\xf6ۣ\xe1//\xecDn\x89v\r\xb7[\xc0\xbcp\x87\v\x90\xae\xfa\x1a\x03\x95\\\xc0\x06\x8f?\x19\xe3N\xdb-\xb7\xfd\xd1/\xbe[^\x84k5\x1a\u007f\x12\xa6\xb1\xb1\xba\v\xb6j\x11\xc3>\xb7G^\x80\xdc\xd6\fK/(6tȾ\xd4\x1c\xa2-Gg\x96s/I\xa0X\xdbK-\x17.\xd9\xdd\xd4\xc7;\x11#z\xb4\xea\x03\xf0~y\x15\xc30\x0f\"@B\xedT\xf0ɤ4\x98\xfb\x13\xcf{\xde\x1f\xcd\x17\xf6\x00?~\xf9\x84\xe9\x1c\xc9 ^R\x8f\x16\xf5\xb1\xe7\xe9\xb4Q\xe0\x05F\x81l-\x8aݴ:\xc6\xf3\xe7\xda\x17 \xe0\t\x0f\u07b3\x1a\f.\x87\x1a\xb1V\xd4 \r\xf2!;\xab\x91'<0\xa8pj\x1e\x05o\x89\xa8\xf8\xf6\x84\x87خ=\xa2\x12~\xe1\xbc\xceS\x97>\xf0*b\xb6R\xd3j\xa2\x86\xbd\x03N\xc7-\x16\x96)\xa5\xaaU\x14?q\xd95\xc3:WEOxxg=\xfbh\xd7\xecd\xb1\x80\x02\xa4\xb0\xc1\"\xef\xb0\xea\x8e\xe4Ad2\xad'\xe3}\xb2\x00⭺\x80/\xda\xd1\u007fn~HK(\xaa\x14>i\xb4_\xb4\xe3/\xafJb\xbf\x88\x13\t\xec\a\xf3\xb6T\xde,\x10]\x16\xcd\xdf\xe0\xc0&\x94D\xb4f\x9b\xb4p\xab(>\xf3\xf4Y¦\x1dV\xc8y\xb4\xf2\xd2\xf2-\x8b\xd2j\xc5f\xba\x9am\x01\xd06^\x81U\xdat8u\xb1\x10\xe2 \x8a\x01\xbd{\xb2V\xfe\x97\xa3\xfb\xa9\xa9f\xb0\xc8D\x82iu\xda̗a\xc2\xe1\xa3L G\xf3\x88P\x90݈\x17\xaa\x05\x9aܷ\x13\xa40\u07b5\xa8Z0\v\x03w;CmE\xbb>\xb2g\xc5\xe6\xa8\xee#7_\xd3\xdd\xe3V\xc9\xe6\x9d\xfd\xa1(\xea\xb7S5\x96Y\x96\x85\xfc:\xf6A<\x92\xde\xfd\xc8\x05\x1fG\xffF\xe6\x95\xc5\xfb\xf78k(\xa4\xb1k\xf8ȉ*\x19\xb6\xc7W\xa7\x84\xad\xa9\xa2@\x12&\xd2\x02\xc9\xc9^d\xe4>\x90\xf2V\x80\x99w&\xf4\xf6ȃ\x8aS1\xcf;m\xbdͯ\x8f\xcbϟ\xf0p~q\xa4\xbd\xceo\xd5y\x1cL\xd2\xf9GJ\xab\xf6Z\xb4\xca\x0epο\x9d\xb3c\xb6d\x8b\x9c\xe0\xbc-\x90\xea讜\x12\xb2$\x14\xa0X\xbb\xf2Zhp\x9d8A.\xfc\xdc*\xa2e\xba\xd0v\xe4\xd6p\x04\xado\xda:\u007f\x00\xd8q\xb7\aN\bc\xa2\xbfpj\bb\xebЀu\xdaTI\n\xa4v{\a\xe4\xc4y;\xcf{bu}\x1a\xe9\x01S\x90y\xdeh\b\xaf\xd3\xcf}\xf6\x02\xfd\xff<̄\x9d%\x86]\x18\x9d\xa0\xb5\xf3\xa2\x14i9f\x0el\xeb\xc3Zჷm\x94j\x8e9J\xae\xda2W\x9cH{B`s\xf3\xa3u\xeeLj\x88\xfe\x8e\x11\xe5Sp\x04N`\xccs\xd1O\x98\x89F\xf7ڏ\xae6`\x00\xe6\x03&\xf3X\xb2RY\xe67\a\x91\xfc\xa39\x1e\xb9T\xb7<\x11|x5g\x05*U\x8e\xa7\x862\xd7\xd5\xf8\x86!\xf5\x87\xd8\xf8\x15\xaa\xb4\v\xcdw5\x06;\x9c=\xbeɈ\xe7\x14\x903\xad\xb4k\x1fք\x99\xdeY\xd8Jc]\x83\xf0\x02\xa8\xd2\xf2\xf5\xf3\xebƘ\xeaƘ\x93C̯~t\xebXq\xa7\x9fC\xb2Ғ\xc0\xba\"\xfeN\xec\x11\xe4\x16\xa4\x03T\x89.\x15\x1fx\x91\xba\xa0i\x16@\xf4L\xf4\xc6$\xd2f\xb6\x06\xab2\x8f'Ȋ\xa5S\xaa\xd9ӱ\xf6\x90_\x84\x8c;\x9d\x82\xd3\xd8\xea\xa6\x12\x82\x86Z7\xcb)d\x06\xb5\xb3\xd2r\xf1C\xe6e\x0e\"'\xb6,\x89\x1b\xb7>\xa7\xa8Ja\xf3\xbc~\x16҅\xcc`\u007f\xb1\xbaL\x9b&:/2tXe\v%ZY\x99b\xed>\x04\xfe\x0f\xe6^\x8d5\x01[!\xb3\xd2,\xd0ы9\xb34n\v\xea\xe9僱xDVL\xcc\xc8C\xf7\x05N\xf3\xbc\xfd(\xcc2\x97\xf9\x9b\xc1\x97wM\v#IJ\xf5\x9cw:\v\x93\xbd\u05eew\x1a\x84W\xa8Ø{:\v\x951ysO\xeb\xf6枾\xb9\xa7o\xeei\xaf\xbd\xb9\xa7o\xee\xe9\x9b{:\xdc\xde\xdc\xd3V{sO\xa3\xedG\f\x86+>\xb9\x9d\xe8\x10\x85Ud\n\xc6\x1c\xda3s\x85L\xa3무\x0e͒\f\xe9\xdb\xe1\x91\x03Y\xfe\x89\xef\xb2◷cRӤ\xae4F\xafN\x99\xa6-Ym&\xff>*\xc2\v\u007f\x81l\xfb\xd8\x04\xba\xb9\xb4\xb9n\xeex\x9d\xae\xe6\xffo\x84 NW\xd3\a\xee\xf9ww휫n\xee\x1b\xc7\x01\x15\xc6\u007fȼ\xf2ȴ\xb6\x99d\xb6\xe9D\xfc1\v_Ѳw\xb9\xd0%\xa6\xe9$~\xff\xa1i\x19\x91m6\x9ec\x16n-щ\xfd\x87u\xf7\x17\xa7C\xc6\xd9\xc8ʞ\xa5\xdb\xf9\xb7x\x14\xba\xaa\xc7vZ{%\xa7\xe1=n\x9f\xc6#\x10\xb5\x01%3π\nB\x87\xfc\xf0\xb5\xf0!\xf2\xc9\xfb|>P\x8b\xcfK[\x9a\x8dV\xe7\x0f\xcd[\x95\x9f\xc8A[\xf6@`6\xdf,\x06i\x88\xc92\x1b\xce\x1f\x9b\x81\xba$\xb7,6\x06\x8f\xc8#\x8b\xcf\x1e\x8b#\x0f\xf0+\xfa\u061c\xb1h\xaf-6?\xecu\xb2\xc2\"s\xc1Z\x19^\xb3 O\xcc\x00\x8b&X\\\xb6Wt\x8eW+sk\x9eZ\x13\x99]\xc3\xf9Z\xb3 \x87\xf2\xb9b\xb2\xb4\xa2p\x8d\xceͪ3\xae\xe6O\x12\u007f*#\xeb\xe5s\xbf_\xd2ϟί\x8aʪ\x8a\x8a\x05\xe6q\x8eʛZ\x9a-\x15Eե\x99Qu\xd6\xd3\xc4\xc4Q\xf9PǹNSK\x99͂\x1a\xcfp\x9a\x02;\x94\xfb\x14\x91\xd74\x01\xb2\x9d\xf1\xb4\xd8\r\x98\x95\xa6\x99\x0eÕ.\xaa6ok\xb3\xff\x0f\t\xfc\xd9Ek\xd3q\x81c\xa2\x92\xaf\xbd!\xc4\xfb\xca\xeb\x1br\xab\xc7c<\xefl\x9f\xe0V\x8f\x80\xbc\xddB^fN\x16Y\xabԄ\xdb\xe1\xa1~\xca\xfe\xab槗\x9b\x03C\xfb\xfa\xbd\x16\xe01\x90\xdd\x00AXx\xc6,\xa3\xff\x1eQ!\xf1\x85]\x12\xbdB\xb29\xe3\xc7\xe0\xe1\t\u007f\xa8\ns\xe1s\xfd\xf8]*۳\x9c U/\xffO\b\xbf\xa6\x9d]\xef\xa3\U000f7fd5h\x0e\xa0\xf7hj\xaffT̚\xf7Jak\xda2kTI\xd0I\xbe\xbcPW\xb5\x8c\xef\x86zC\xc3G\xe5\xcdl\x1fW\x86E:\xa4\t\x8e\xa6T'\xc5Bc \x94\xae!\x8c\x8c\x8f\xf1\xa5\x97<\xe0y\x8dP\xe9%\x82\xa5(\xb7\xe25\x02\xa6\xd7\n\x99\x96\x06MK\xae.\xa3\x1e\xe0\xbcF\xe8\xb4$xZ\xe4\x01\xc6?\xb0y\xad\x875\xaf\x10D\x9d\x1cF-\"]\xecÙ\xc5\xc1T\xc4\xfaf\x1e\xca\x1cy\\\x11 G\x1f\xc8\f\aT\x11\x10\x8f\x1e\xc6̆T1\xfb\xa0\x1ft\xfd\xf43\x97\xe8k\xfcEwI\xb1W\xf0q\xd7<\xf3\xcfW\"\x9f\xadD^\x02\xc5`\x1f\xf9\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..14a8ba2311 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 v2.0. // +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) }