diff --git a/changelogs/unreleased/8987-kaovilai b/changelogs/unreleased/8987-kaovilai new file mode 100644 index 0000000000..26771dc16c --- /dev/null +++ b/changelogs/unreleased/8987-kaovilai @@ -0,0 +1 @@ +Make ResticIdentifier optional for kopia BackupRepositories diff --git a/config/crd/v1/bases/velero.io_backuprepositories.yaml b/config/crd/v1/bases/velero.io_backuprepositories.yaml index 2ac737063e..19e4ce0dc3 100644 --- a/config/crd/v1/bases/velero.io_backuprepositories.yaml +++ b/config/crd/v1/bases/velero.io_backuprepositories.yaml @@ -71,7 +71,7 @@ spec: resticIdentifier: description: |- ResticIdentifier is the full restic-compatible string for identifying - this repository. + this repository. This field is only used when RepositoryType is "restic". type: string volumeNamespace: description: |- @@ -81,7 +81,6 @@ spec: required: - backupStorageLocation - maintenanceFrequency - - resticIdentifier - volumeNamespace type: object status: diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 9f8a5b6898..7bc100e51d 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -29,7 +29,7 @@ import ( ) var rawCRDs = [][]byte{ - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xccXO\xaf\xdb6\f\xbf\xe7S\x10\xdduNV\f\x1b\x86\xdc\xdal\x05\x8a\xb5\xc5CR\xbc\xbbbӉ\xfadI\x93\xa8tٟ\xef>P\xb2\x13\xc7V\xe2\x977`\x98n\x96H\x8a\xe4\x8f\xfcQIQ\x143a\xe5#:/\x8d^\x82\xb0\x12\x7f'\xd4\xfc\xe5\xe7O?\xf9\xb94\x8b\xc3\xebٓ\xd4\xd5\x12V\xc1\x93i\xd6\xe8Mp%\xfe\x8c\xb5Ԓ\xa4ѳ\x06IT\x82\xc4r\x06 \xb46$x\xdb\xf3'@i49\xa3\x14\xbab\x87z\xfe\x14\xb6\xb8\rRU\xe8\xa2\xf1\xee\xea\xc3w\xf3\xd7?\xce\x7f\x98\x01h\xd1\xe0\x12\xb6\xa2|\n֡5^\x92q\x12\xfd\xfc\x80\n\x9d\x99K3\xf3\x16K\xb6\xbes&\xd8%\x9c\x0f\x92v{s\xf2\xfam4\xb4\xee\f\x1d㑒\x9e~\xcd\x1e\x7f\x90\x9e\xa2\x88U\xc1\t\x95s$\x1e{\xa9wA\t7\x12\xe0\v|i,.\xe1\x13\xfbbE\x89\xd5\f\xa0\x8d4\xfaV\x80\xa8\xaa\x98;\xa1\x1e\x9cԄneTh\xba\x9c\x15\xf0\xc5\x1b\xfd h\xbf\x84y\x97\xddy\xe90&\xf6\xb3lГhl\x94\xed\x12\xf6f\x87\xed7\x1d\xf9\xf2J\x10\x8e\x8dq\xe6\xe6g_?\x1f-^X9'\x02zgɢ''\xf5nv\x16>\xbcN\xa9(\xf7؈e+k,\xea7\x0f\xef\x1f\xbf\xdf\\l\x03Xg,:\x92\x1d\x10\xbdA\xc7b\xc0\xefMP\x15\xa7\xef\x01\x1d\x81\xc3\xd2\xec\xb4\xfc\xe3$\xdb\x03\x99\xa8T\tBO\x10Q\xd4B\xc1A\xa8\x80\xff\a\xa1\xab\x81\xe4F\x1c\xc1!넠{\xf2\"\x83\x1f\xda\xf1\xc98\x04\xa9k\xb3\x84=\x91\xf5\xcb\xc5b'\xa9+\xca\xd24MВ\x8e\x8bX_r\x1b\xc88\xbf\xa8\xf0\x80j\xe1\xe5\xae\x10\xae\xdcK\u0092\x82Å\xb0\xb2\x88\x8e\xe8X\x98\xf3\xa6\xfa\x9fk\xcb\xd8_\xa8\x1d\x01\x9dN\xac\xa4;\xe0\xe1\xd2\x02\xe9A\xb4\xa2\x92\x8bg\x14\xf8\x8aC\xb7\xfee\xf3\x04\x9d%\t\xa9\x04ʙt\x14\x97\x0e\x1f\x8e\xa6\xd45\xba\xc4W;\xd3D\x99\xa8+k\xa4\xa6\xf8Q*\x89\x9a\xc0\x87m#\x89\xd3ව\x9e\x18\xba\xa1\xd8Ul\\\xb0E\b\x96K\xa7\x1a\x12|а\x12\r\xaa\x95\xf0\xf8/cŨ\xf8\x82Ax\x11Z\xfdv<$N\xe1\xed=t\xad\xf4\n\xb4\xc3\xf6\xb8\xb1X2\xb2\x1c\\f\x95\xb5,SM\xd5Ɓ\x18\xd1_F*\xdf\x02\xf8\xa4&\xba!\xe3\xc4\x0e?\x9a$sH4\x95v|\xde\xe5\x04u\x16s\xdbJ=\x01\xf3\x84\x19\x81\xb4\x17\xd4k\x06$\xa4>\xf5\x94\xac\x937\x90\x89\xe8\b\xee\x14Z\xe8\x12\xdf\xc7|\xd4\xe5q\xc2\xd1O\x19\x16vio\xbe\x81\xa9\tu_hkkƓ-\x82\v\xfa.c\xcf>\xae\x8c\xae\xe5nlh\x7f\x90]\x03wB\xc9\xc0\xdb\xf5@'{\xca\xc9u\xb6\xa5\xe82\x8f\x01\xa9\xe5.\xb8k\xe0\xd5\x12U5j!\x00:(%\xb6\n\x97@.\xe0\x95\x88\x8cj\xe52\"<\x1f'\x80[_\x10\x83\xd4\x15WK;\xacXI\x97\x8c\x9c\xfe\xa8+p\x97kJ\xff\xa0\x0e\xcdX]\x01\xcf\xc6J\x91\xb9w\xe8I\x96\x99\x87\x87\x87\xfb2\x80\xc5|\xa8\xb8\x1d\xd5\x12\xddkjr=\x90ѕc\x1d\x94j\x15\x14\xa5i\xac \xb9U\xd8\xcd\f\xc6\\&\x9ec.i`T\x86\xf0\x14'\x01c\xce*\x8cVG\b\x1e+\xf8\xb6G=\x02\xc3\xc3C\xd2\xfdpWI\x1cxQ\xc3\xd3j\xf7\x9ax|\xb9\x14\xd1\xefN\xe9\":\x96ZbϿ\xae\xfd\xf8\x8cHk\xaaֲ\x96/\xd6\xcc\x1d\x8eq_\x91\x0e\ac\xbe\xc87\xe6\x01M\xae\xa5\rH\x06Q{\xd1d\"A\xc1\xdf3\x9b\"C\x17\xcd28\x17g\x7f\xba\xe5\x95\xef\xd5\xd3I\tO\xbd&\xcc\v\xf8\x04\xee\x1f\xc7\x1c\x9da,\f\x88/\x18\xda~\xf02\xb8\xfaP\x96\x88\xd5x\x1d\x01Ʒ\x11\x94\x16\xfd\x82彮\xcb\xe5\x87\x14z/vSN~JTi\xd3kY@lM\xa0+\b\xd0>\xe7\xe3mT&,\xb5{\xe1\xa7\xec|d\x9a\\^\f\x96\x81[&\\k\xbf\x9f\xf1[\xe6v\x8d\xa2\x1a\xb7\xf0\x02>\x1b\xca?\xdd\xec\xc0%\xea~2M\x0e\x9d\x01={~\x81A+r\x94\x7fc\xaf%a\x93\x1d\xe7\xd7k%\x1dn\xe7\n\tO\xbfU\xf3d\x03\xd3WC\xae\x13h\xe9\x81W\xb9X9Ws\xa9\vٔc\xe9L\x97P:\x13\x85\x94\xce\xcd\r\an\x15U&\x12\xf7\x96\xd6\xd5P$\xb8_\x16\x8eI\x0f\x1c\xfa\xa0\xe8E\x0e\xac#i\x87_b<\xa7\xdf\xcb\xec\xc9\xd7\\:\x05l\xba\xd6x\x95⽐\xea\xea\U000e4cde\x84\xa3\xfb\xf2ws\xc1r\xfa\x9dķ\xfd\xbc\xfdO\xe6獝\xb7{\x14Ή\xe3\xf4\xe8\x1e]z\xfe\xc9^\xf5\x8c\xf3i\x9d\xe8߄\xed\xe9\x1f\x89%\xfc\xf9\xf7\xec\x9f\x00\x00\x00\xff\xff\x18\xd9g\x90\x9b\x14\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec}_s\x1c)\x92\xf8\xbb?\x05\xa1\xdf\xc3\xecntK\xeb\xf8\xdd]\\\xe8\xcd+\xdb;\x1d3c+,\x8d\xf6\x99\xae\xca\xeefDA-P-\xf7\xde\xddw\xbf \x81\xfa\xd3EUQ\xad\x96ƻg^luAB\xfe!3I\x12X.\x97oh\xc9\x1e@i&\xc55\xa1%\x83\xaf\x06\x84\xfdK_>\xfe\xa7\xbed\xf2j\xff\xf6\xcd#\x13\xf95\xb9\xa9\xb4\x91\xc5\x17вR\x19\xbc\x87\r\x13\xcc0)\xde\x14`hN\r\xbd~C\b\x15B\x1aj\x7f\xd6\xf6OB2)\x8c\x92\x9c\x83ZnA\\>VkXW\x8c\xe7\xa0\x10x\xe8z\xff\xe7˷\xffq\xf9\xefo\b\x11\xb4\x80k\xb2\xa6\xd9cU\xea\xcb=pP\xf2\x92\xc97\xba\x84̂\xdc*Y\x95פ\xf9\xe0\x9a\xf8\xee\xdcP\xff\x82\xad\xf1\aδ\xf9\xa9\xf5\xe3\xcfL\x1b\xfcP\xf2JQ^\xf7\x84\xbfi&\xb6\x15\xa7*\xfc\xfa\x86\x10\x9d\xc9\x12\xae\xc9'\xdbEI3\xc8\xdf\x10\xe2G\x8d].\xfd\x80\xf7o\x1d\x84l\a\x05uc!D\x96 \xdeݮ\x1e\xfe\xff]\xe7gBrЙb\xa5A\xdc\xff{Y\xffN\xfc(\tӄ\x92\ađ(Orbv\xd4\x10\x05\xa5\x02\r\xc2hbv@2Z\x9aJ\x01\x91\x1b\xf2S\xb5\x06%\xc0\x80n\xc1\xcbx\xa5\r(\xa2\r5@\xa8!\x94\x94\x92\tC\x98 \x86\x15@\xfe\xf0\xeevE\xe4\xfa7Ȍ&T\xe4\x84j-3F\r\xe4d/yU\x80k\xfb\xc7\xcb\x1aj\xa9d\tʰ@tWZ\x92\xd4\xfau\fW[,y\\+\x92[\x91\x02\x87\x96'1䞢\x16?\xb3c\xbaA\x1f\x85\xcc\xfeL\x85\x1f\xfe\xe5\x11\xe8;P\x16\f\xd1;Y\xf1\xdcJ\xe2\x1e\x94%`&\xb7\x82\xfd\xa3\x86\xad\x89\x91\xd8)\xa7\x06\xb4\xa5\x8c\x01%('{\xca+XX\xa2\x1cA.\xe8\x81(\xb0}\x92J\xb4\xe0a\x03}<\x8e_\xa4\x02\xc2\xc4F^\x93\x9d1\xa5\xbe\xbe\xba\xda2\x13\xe6W&\x8b\xa2\x12\xcc\x1c\xaep\xaa\xb0ue\xa4\xd2W9\xec\x81_i\xb6]R\x95혁̲\xf9\x8a\x96l\x89\x88\b\x9cc\x97E\xfe\xff\x82x\xe8N\xb7\xe6`\xc5V\x1b\xc5Ķ\xf5\x01\xe7\xc7\f\xf6ة\xe3\x84сr(6\\\xb0?Y\xd2}\xf9pw\xdf\x16T\xa6=SZ\xf2:\xc4\x1fKM&6\xa0\\\xbb\x8d\x92\x05\xc2\x04\x91;QE9\xe7\f\x84!\xbaZ\x17\xccX1\xf8{\x05\xda\xce\x01y\f\xf6\x06u\x10Y\x03\xa9\xca܊\xf1q\x85\x95 7\xb4\x00~C5\xbc2\xaf,W\xf4\xd22!\x89[m\xcdz\\ّ\xb7\xf5!(\xc8\x01\xd6:\xc5rWB֙h\xb6\x15۰\xccM\xa7\x8dT\x8d\xdeq:\xb0K\xa1\xf8Է%\xd3\xecN\xd0R魯g\x05\xc8\xca\x1cט\x925d\xde\xdd\xea\bJ\x18\xa1\x1f/\xea\xacJCn'\xed\x13e\x06\xc7|s\xb7\"\x0f\xa8\xacBkTZ\x95&\xa6R\xc2JI\xa4\xaf/@\xf3ý\xfcU\x03\xc9+\x14\xeeL\x01\xd2aAְ\xb1\x92\xa0\xc0\xb6\xb7\x9f@)K\x1b\x8d\x03\x90UO\xd9\xd8r\xbf\x03K[Zq\xe3\xe7\t\xd3\xe4\xed\x9fI\xc1Dez\xa26\xc8u\xa4\x145\xb4\x90{P\xa7\x10\xf1=5\xf4\x17\xdb\xf8\x88v\x16(A\xa8\x96xkO\xc7\xf5\x01?Ƹ\xed\xcajӂ\xc84\xb9\xb8 R\x91\vg\x81/\x16\xaeuŸY2\xd1\xee\xe3\x89q\x1ez\x99\x87\xbc\xa3\xa1c\xa8\xbe\x97\x1f\xb5\x13ޓh1\x00\xabE\x9a\xa7\x1d\x98\x1d(R\xca\xda\xe2m\x18\a\xa2\x0f\xda@\xe1\t\x13\xac\x88\xc7'\xd2\x13\xce\x1d\xce=\bm\xe9\xea\x11\xe9#/*\xce\xe9\x9a\xc351\xaa\x82\x01ڬ\xa5\xe4@\xc5\x04q\xbe\x806,;\ai\x1c\xa4\ba\x94\xffС\x00\x1aM\xfa\b\x84F@{\x9aY\xeb\xccy\x8b\xb0]\xaaD\xc7T*Ȭ־\xf6ր\x01G\v$$\xe1RlA\xb9ޭ\xa7\x12\x04L\x81\x15\xb8\x9cXE\xab\x80[kB6\x95\xd5\xc1\x97\xc4\xce\xeeA\x19`B\x1b\xa0\x11\xe1|\x06\x7f\xe0kƫ\x1c\xf2\x1b\xe7x\xddY\xff1\x0f^sOk\xa6\xf0\xe9\xc3(Do\x9d9\xcb\xd0\t\xf4\xfe\xde\x12\xfd֘\x986F\xfaP\x82s\x9d-+\xfd\xb0\x1b\xeb;\xaa\x0f4\x18\xdb\xe8\xe2O\x17\v\xe4p\xb7\xd7n\x1f\x9aP\x055Y\x92\xf5&\x14\xa59\xf4k3\x03E\x84\x8a\xa3\xfa$\x91\x9fT)z\x18\xe0f\xed\xff\x9f\x91\x9fC0\x8f8*B\xb5W\xe6\xe9q\xbf\xff\xca\\=\x0f\x1f5\xaev)\x13\x96\x7fv\xe1\xd9a\x9fv\xeb7K6!M\x04\x1e\x13\x0e\x1e.\xcdF\xb8\xf5;\x11\xeb,2?$\xe4\xb5ly\xe1\xfd\xa7\xa4\xd4N\xca\xc7)\xea\xfch\xeb4\x8b\"\x92aT\x85\xacaG\xf7L*\x8fzcj\xe1+d\x95\x89\xcezjH\xce6\x1bP\x16N\xb9\xa3\x1a\xb4[&\x0f\x13d\xd8}'-5\x12\xfdx\x84G\xc3H\xcb&\xc4|h\xe8֏8\xb6\x92\xa1\u0601Z\xf7\x1a\x8dq\xce\xf6,\xaf(G\xbbLE\xe6\xf0\xa1\xf5\xb8bZf\x84ɽ1G%\xd3\x15\xe7\x10\x04\xa4,\x93:+%)\xc0\xfa\xbc\x85]\x13\xf4\xab\x0ec\xbe\xa6\xd6W\x91C\xd8\x13d\x96\xaa8h\xdfU\x8end\xa33\x16\rS0\x10A8]\x03'\x1a8dF\xaa8E\xa6\xf8\xecJ\x8a\x12\x1c dD\xf3uW\x1a\r\x02# \t.\xe1v,\xdb9W\xcf\n\x11\xc2!\xb9\x04\xeb\xf0\x19B˒G\xccESF\x99\xef;\x19\x9b\xebM\x99\x98\xf5\xc7\xf0b\xf3\xbf)\t:\xb3)Q\xd26\xf3\xabK\xd9Z\x1c\xe2kڦ\xfck\x126h\xfe\x13\x84vd\xf6\x13\x8c\n%\xcb\xf4\xa0\xdcZ\xaa2З֝BOgA\x98\t\xbfN̈́\x8e\xcf\xd5\v\x96u\x88\xf0m\xf3f\xbe\xd0'\xb2&eN\xbc\x10c\xea.\xfe\t\xf9\x82&\xe3\xce[\x8cd\x9e\xfc\xdcn\xb5 lS\x13=_\x90\r\xe3\x06\xd4\x11\xf5OR\xf5\x813\xe7 F\x8a\xd5#\x18\xbe7\xd9\xee\xc3W\xeb\x82\xe9f\xa7*\x91.Ǎ\x9d#\x1b\xbc\xfd\xaey\x9e\x80K0\x8c\xcd\x14\x14\x18\x1e\xc7\x15S\xfb\x17t\xad\xde}z\x1f__\xb5K\x82\xe4\xf5\x10\x99\x98t\xae\xbc;¨=>\xef\u0087/\xe8\x03\xd5\v \xb7\x15\xb2 \x94<\xc2\xc1\xb9.T\x10\xcb\x1f\x1a*'t\xaf\x00\xf7dP\xce\x1e\xe1\x80`\xe2\x9b,\xfd\x92*\r\xae<\xc2!\xa5\xda\x11\r혘\xf6\x9bG\x96N\xf6\a$\x04\xc6\xd6S\xc5\xc0\x15?\x15\"[\x1a\xf1\x92\xa8KB\t\xb4?\x01\xcd$Qi\xf7\xd1ޥD\t\xf8A;^\xda\x19\xb3c%\xaaU\x8c8\xc8M2C]y\xa0\x9c\xe5uGn\x8e\xacĂ|\x92\xc6\xfe\xf3\xe1+\xd3~#\xf3\xbd\x04\xfdI\x1a\xfc\xe5E(\xea\x06\xfe\x92\xf4t=\xe0D\x13N\xcb[\x82\xb5\xb7\xe2\x9cM\xb3\xd2VӞi\xb2\x12v\xb9\xe2H\x92\xd8\x15\ueeba\xee\\GE\xa5q\x17MH\xb1ta\x9bXO\x9e\xdeRu\xc8\xfd\xecN}\x87\xf7\xd6X\xb8/n\xef\x97\xd3\f\xf2\xb0]\x83\x9b\x92\xd4\xc0\x96e\x89\xfd\x15\xa0\xb6@J\xab\xc2\xd3$\"Q\xb1zl\xe6\x89O\x9a\xf5n\x97\xaf\xcb\xc7z\x8f\x7fiM\xce\xd2C0\xb2H\xa0\x81\xd7\xdd\xf94>K;g\x13j\x05I\x98\xac:\xb0g9\\5\x85(\xcf \aZqtq&\xb9K\xf3\x1c\xf3\\(\xbf\x9daQf\xc8\xc2\\\xd5\xd0\x1a\xbb3\xc1\x05ŭ\x96\xff\xb2\x96\x16g\xd3\xff\x90\x922\xa5/\xc9;Li\xe1\xd0\xf9\xe6\x83f-0\t]bJ\x8a\x95\x9f=\xe5\xd6\xf6[\x05.\bp\xe7\t\xc8M\xcf/Z\x90\xa7\x9d\xd4\xcelכ8\x17\x8fpp;\x86\x93]\xb6\x95\xcc\xc5J\\8\x1f\xa2\xa70j\x87C\n~ \x17\xf8\xed\xe29\xaeT\xa2\xa4&V\xeb\x88hA\xcb4\tŔ\xa2TG\xdd.X\x83\x13b\x1b֩2\xd6\xc9\x1e\xc36IDK\xa9#\x1b\xf9\x03C\x99\x10\xde[\xa9\x8d\x8b\x97u|\xe6h@M\x86 \x1a\xa1\x1b\x97\xbf$UH6\xb1Jy*\xf4\xdb.\xf7;\xd0\xe0\xf7+|`\xce\x01\xb5+\xbb\x8bf~;m\x7f\xe1\xf6K\xb0\x13\x9a\xa1ǂmK%3\xd0ѽ\xec\xa6$؋HVF\x1b\xf7:\xe6H\xdd*ɥd\x8c\x87@CIwy-!f\xae\x17>|m\x05D\xedܷ\x7fO\xc9\xd8\xdcq\x11L\x19,\nz\x9c\xa6\x944\xc4\x1b\xd72\xcc\x06\x0f\xc8->ԶBM\x90j\xcbk\x01\xfc\x16\x1c\x85\x82\x89\x15v@\u07be\x80c\xe1uh,\xd9$VNseoB'\rw\xea\x1f\xdcT.%n\x15(\xe80\xaf\x1fUG?TH\xd3\nH\xccp7K\x99\xff\xa0Ɇ)m\xdaC\xd0\x03i*Q03\x17^\xe2\x83R'\xad\xbb>\xbb\x96\xadp\xd7N>\x85\xf4,G\x98D\xccq\x7f\t\b\xdb\x10f\b\x88LV\x02\x038v\x1ec\x17\x8e\xb8Nò\xd4I\x926\xfbm\x01Q\x15i\x04X\xa2\xa401\x1a\xe9iW\xffH\x19\x7f\t\xb6\x99\xa1,\xb6X9mN\x84\x14\xb7vB^A\xbf\xb2\xa2*\b-,\x8fИ\xb3\x02\xbaLo\x12\xdfl\v4\x13F\xda\x19Sr0\xe0\x93\xd7\x12ǐI\xa1Y\x0e\xb5q\xf5\x82 \x05\xa1dC\x19\xafT\xa2\x06\x9cE\xde9K\x11\xaf\tη\xc6H\xeb|\x89\xa4H\x88\xe6&\xfa\x8a\xe3ڸT\xe9\x1eߔ\x9b\xa5`\xbe\x97U*&1-\xf0̎\x96O\xa4\xa4\xe2\xf0\xdd\xd3J\x1d\xeawOk\xac|\xf7\xb4&\xcawO뻧\x95R\xf3\xbb\xa7\xf5\xdd\xd3j\x97\xff\x13\x9e\xd6Ԉ\xdcy\xbe\x81\x8f\x93\xa3Hت\x1e\x1b\xe2\b|\x9f\\\xe1s\xc0\x9f\x95\x8b\xb9\x8a\x83\x8a$\xfe\x0f\xa4uǔVc<\xea\xe4L;k\x82̻\xe3E\x13\xae\xe43\xb2\xeeC\xa7\xe7˺_\x8dBB\xbe\x89\x8c\xc0i\xe4;Ɂ~\x13\x1a\fݿ\xbd\xec~1ҧ\n\x92'fv\x11@O;\x10\xb8\xc3.\xb6\xed\x03\x00\xe1>\x02\x7f0\xffX\xc0\"\x80\xa4\"\x82q'y\xf5m\x06m\xb9#\x9fK\x17{\x9a\xedw\x8c\xc7TҒ\tON!\xec\xa6\b\x0e\xf8\xa5sw\xbb\xcfrd\xe2wI\r\x9c\x9f\x10\x98\x12\x11\x9bH\xfe;!\xe5/1\xb7\xf8\xd9\xdb\xf3)I}sV\xcc/\x96\xc0w\xfe\xb4\xbd$\xfaL\xa7\xe8͡\u038b\xa7\xe3\xbdb\x12\xde\xeb\xa4\xde%&ܝ/s>-\x1e{R\xe6\xd8t\xe8`8in2Un2\xb40\x85\xd8l\x94&S\xe0\xe6$\xbeMr'm\x9a\xbdZj۫%\xb4\xbdn\x1aۨ\x14\x8d~\x9c\x93\xa8\x16\xbf\x96\x86L\x1a[\xfeZ\xc2v*\x19\xa4긯'\xad\xaf>\x1f\xc1\xb0\x8c\x0f\xae\xdd+\xf9\xc8E\xc5\r+9n\xa4\xeeY\x1e\r6\x98\x1d\x1c\xea\v4~\x93x\xf4\xd4\xdf\x04\xf3\xf9K-\xb5\x97G\x9e>\xd5\xe4\t8'46\xafz\x98g\xee&\xa6L.\xc1\xda#;;\xfd\xc5 \xfe\xfa\xa6\x85\x13w<]\x8bV\xad\x88\x85\x98\xa8\x18\xbeEf\xd0p\xa4蛞\a\xeb\xfcp\xfc\xed\xef\x15\xa8\x03\xc1{lj?\xa79\x04\xe6'\xa6\xb6\v\xb1\xa0*\xbc\xda\x1a\x8a\x9f\xf7\x9c\xfef*\x93w\xc2Y\xdd\xe3\xf1`\x1b\xab#\x9aE\x8dU|v\xbd\x12\xedc\xa0\xb9\x90u\xebH\xb3)\a9\xf5\xb4\xd4\xcb.q\xe6/r&\xbd\x8at\xcf\xefw:\x05u\xca駴\x04\x80\xc9\xd3N/\xb5\xe4\x99Z\xf4$\xfbyi\xa7\x99\xe6m\x16\xbe\xe0饗8\xb5\x94H\xa9\x94SJ\xf3\xe8\xf4\n\xa7\x92^\xf54\xd2k\x9dBJ>}\x94\x94Ⓖ\v\x9c\x9a\xa2r\xe2q\x9a\xe9=\xde\xf1\xd3D\t\xa7\x88\x12v\x7f\xa7\x91<\x01\xbd\x84SB\xf3N\a%\xf0,u*\xbe\xe2)\xa0W<\xfd\xf3ڧ~&$k\xe2\xf3\xbc\xd3='oYH\x95\x83\x1a\xdd\xf6I\x95\xc2Q\xf9KY\xdbt\ar\xb4\xdf\x11n\xfd\xb3\xb5:\xfe2\x9a\a\x7f\xd1(^);\xb4}i%\xad\xe5mt\xf6\xa2\x1a\xf7\xa7\xebL\xfa{f\xddv\x95\x86\x92*\xbc\xbbx}p\xe9,Q\xd3\xfc\x81f\xbb#\xe8;\xaa\xc9F\xaa\x82\x1arQo\x00^9\xe0\xf6\xef\x8bKB>\xca:'\xa2}/\x8ffE\xc9\x0fv\x85B.\xda\rN\x93\x80\xa8\xb4\x85\xden%gY\xc4w\x8b\xde\xcd\xe4*\xf7.\xcb\xc0\x1b\xa3\xb2v\xca@i+\xc6]7t\xf3\xbaW`n$\xe7\xf2i\xe6ڟ\x96\xec\xafxs\xf73\xa2C\xefnW\b#\x88\a^\x05^'g\xd5ج\xc1\x9a\xe5\x06ϡ\xb9\xbf\xdat v\xf3\x1cۗ\xe3B\xee\xeeA\x0en\x81W\x9d\x99\xb4\xda\xe5v\xe5\xc61ԋ\x95\x19*\x0eDbF\x8d\xd91\x95/K\xaa\xcc\xc1%j,:c\b\xb6t,\xba3h=\xfaw;G\xc9\x1b\xaet\xc6\x1d\xcaC\xd9\xdd\xf4=\xa6\xdd)\xe3\x18>\xbd8yn\xf1\x8c\xe3\x18vK\x96H\xa9\xc8\xcf\xd1̯\xb3Eʹ\xbf\x99\xf8\x17\xb9\x87\xf7\xd1\xe8Y\x872\xb3\x19\xfe葫mT\x80\x7f\xe6\x1c\x04\xeb\xf3p\x0e\xfc#\xe3\xa0ݰ\x12\x14\xf0m\xbfU\xad\x8f\xabb\xed|\xb8\x8d\xfdXw0`\xe3\x1cZ\x18\x8a.AY/\xca\x05\xad+\x1ddu\x18\xf1\x86#L\x18\xd8B\x7f\x158\xa2\x81ݭ\xd2h>\x83:\xc1\xb5\xccO\xb1\xf8V\a\xf9\x87\xe1\x96G\x9cl\x85\xbcb7\xee9'\xe4\xf6\xe1F\x93J\xe4\x18.~\xf8\xeb\xdd,\xa9\xdbwn\xae\x0f\xb3uJ\xa9>\xc4[\xb5\x9c㖾pޱ\xdcD\x10\x18\x82\xd3z\a\xe4\x89\x19\x7fq\xd7yoZ\x1dZ\xf2\f\xbdp\x80W\xfaO\xbfq\xe0n\xfe\xf7/\xa3\xf8\xe9X)\xbc&տ\n\x80\u05ca\x9e\xf4\xcc\xc1\xbaNت\x93\xbf\xf4;c\xa0(M\xccטV\x87\x7f\x19\x03X\xfbi\xd2Pޚ\x954T\x88y\xda\xfa \xb2\xb1\xc42\xaf\x8dF\xb896\x1fc\x04\xb8\xf1\xe7!\xceF\x80\x1a\xe0\x10\x01t\x95e\xa0\xf5\xa6\xe2\xfcP\x1f\xc7\xf8F\xa8\xf1\x912~>R8h\x83\x82`\xd1\x1b\x854\x89\xb0O\xf7\x06\x91\x87\x99\x1e\x8e*\xcd#\x85\xe7\x82φԆ\x16'=\xd8p\xd3\a\x83O\xf6\xa8\xbc\x95TI\xeb\xb1Sݰ?f\\\x1ap\xae%.\xb2,4\xc8\t\xecA\x10k\x9d\x1d\x89ÛS3\xa1\xf8\x13\xae\xce\xc2\x05{\x17B!ч\x89\x88\x8fvh|\x00\xe7\a]\xc3\xc4\\Q|ϤO\x84\xbe\xf3\xeb\xa2\x15\xd7\xd6\xfb\x87\xa5\x05q\x9a\xd7\x1a\xd5͙f]\xbb\xf0<%ws\xb7\x1a\x02w\x8a\x8a\xeb?\xf7\xf2\xcci\xdcG\xf7Y*\xad\x8f\xee,\x85\x16\x81X\xcb\xf8\xf9qǩ~ڥ\xee\xd8\xd29\x1cY8CG9\xf7\a\x1d\vКn\xc3m\xeeOv\xe9\xb1\x05\x01.<\xe76O\"@\x9bSqݻ\xccݔ\xa1\x99\xa9\xa8\xef $\xf8\xb6j\xfd\xa0\t\x971\xa8\xf8\xa0\v\v/\x85\x855\xd9LB}-\x99JY\xc3}\xa8+Zڠ'\x8c\xdci\xdev\x03ζ̮u,\xe7\xb6T\xad\xe9\x16\x96\x99\xe4\x1cP[\xf7\xc7\xf5\x92sݟ=\xfc\x02TO\xa2\xf6\xb1]\xd7\xef\x00:n\xbb\x8do\xea\xd2\xdd\xf1\xf5.\xc3\x144\x0f\xe9\xf5\x06$\xb1\xe3Y\x8e\xb2\xa3B\xf4\x95\xb9\xfeH\xdbuì\xf3j\xd9\xc7y\xfd#s\v\x1f\x17\x88\xcbcA\x7f\x93jA\n&\xec?T\xe4n\x03/4\x9e5\xfe\x9d\x94\x8fw\x11'\xb67\xf8\x1f\xeb\x8a\xcdV\a\x13n\xd8x`t-+\xbf\xfb^;\xb4\xf1m\x15\xbc\x99\xff\xcc\xcbM\x849b\x0fz\xe8\fFt\x7f\xec@\x9a4\x05\xae\xe7\x01Xw\xe1%3\xce\x0f\x8bc\xc8G\xaf&6\xb0[/\x17x7\xa0\xb9\x8f`\xa0\xa3\xb0#\x15\x05R_|\xd1V觬z=\x99\x87\x9c\xc9\x1e\x8d\x7flj\x0f\xd1\xd1\r\xb3\xe5\xee\r \xd8q\x02ϻ`\xc7g*&\x84\xff\xd6֩\xef.h-\xdcB\x96\xd8`\x94.~\xf6}I>A\x7f\xbb\xc2\x1dg\x87\x1c33pVE\xaa\xacĭ\x92[\x05\xba/tK\xf27\xca\f\x13ۏR\xdd\xf2j\xcb\xc4\xe7\xe1\xa3;c\x95o\xa92\xcc\n\xad\x1bOl\xa0LP\xce\xfe\x11\xd3O\xed\x8fӀn\x06\x17JK\x920\x8c\xa1\x0f\xef\xc1\xfa\xaa\x83\xeb\xfb\xa8*,=]O\xf1;\x02O\xa6tc\xed\x134>E\xe8\xf6\x92|\x92\xd1\t\xeeӚX\x17\xa6u\xad@\x9b%l6R\x19\xb7\xeb\xbc\\\x12\xb6\tA\x04\xab;0\xfe\xe5ޜ$,\xb6]\\'\x8c4f\b\x83\xd7\n\xad)^I_Ѓ\xdba\xa2YVYO\xe9J\x1b\xca#\x8eʳ\x148Fk\xec$\x82\xfc\xd7g\xedȭڀ\xfa\xc1C\xecǑ\x14/\xc5p\xde\x1b\xb7(\x82 O\x8a\x19c}#9\x92\x12\xe0Ie\xac\x8f\xc49і\xd4'E\x11\x89S\x87\xab\xe1Ԛ4\x94\xefk(Cj\xd6c\x8d/,\xae\x916\xc4\xfa\xaf\x98E\xe4kY6g;*\xb6\x837\r씬\xb6\xbb \xc9\x03N1\xc9+\xc0\xa0+\xaa\x14\x1d\x1e\b6\x95\x12\xad\x94\x80\x91\xe3\xdb$\b\x03\x0e\x97f\x8f\xa4*\x17\xfe\x01^\xff\xbe\xf2\x95\x7f\xcbd\xb9Q\xb2X\xfa~1&\xba\xf0;\xf2\x8aI끘]\x94\xea\xc4y\xdf\xfe\xb9\x00\x94\x84\xb2\x04A\xa8\xf6='\xdc\xf8t\xb2\xb9ц*\xf3\xacp\xc4]\a\xc2D$\x02\xbb\x8b#q\xe7\x13\x13ܕW7\xfe\xa1\xd1\x1a\xf0\x82h&\xc2\x13\xcf.\xc9\xc1\xc9Gt\xcfK\xe0\x93\x8cR\xc5\xf3\x0e\xc7C\v]\x84^7\xaa\xb0\xafm퇓\x17\x9d\x0fG0\x8e\x8e/\xe3\v\x9cu\x95\xb0P\xfc\x03\x8bE\xbe1a5\xb3\xa8\xfc\xf1w?\x96\xbcOZ\xd4\xc4)2\xb6\xc6\xc1\xe5\xcb\xf0b\xa5\xfb\xe2\xe6-\a\xebzi\x80\xee\xf2i\xd62y\x7fƸ\xd19\x83F\xe11\xf3\xf3DM\xf6g\f\x17\xbdX\xac\xe8\xbc(?Q|\n\xf9\xa4Y\xfb7\xdf6\x12,\xf2`\xcf\x1d.jE\x8b\xc2\xc0_5^\x14\xb5J\xbd\x1fQO\xe7-m\xe1{\xf2\xbf\xfco\x00\x00\x00\xff\xff\xaf\x05\xd1\xf3\xa2\x81\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xccYK\x8f\x1b\xb9\x11\xbe\xebW\x14v\x0f{ٖ\xec\x04\t\x02\xdd\xc6r\x02\x18\x19\xc7\x03k2\xb9.EVK\\\xb1\xc9\x0e\x1f\x92\x95\xc7\x7f\x0f\x8a\x0f\xa9\xd5\x0fK\xe3\x04\x9b\xe5eF|\x14\xeb\xf9U\x15\xbb\xaa\xaa\x19k\xe5\vZ'\x8d^\x02k%~\xf1\xa8闛\xef\xff\xe0\xe6\xd2,\x0eog{\xa9\xc5\x12V\xc1y\xd3|Fg\x82\xe5\xf8\x1ek\xa9\xa5\x97F\xcf\x1a\xf4L0ϖ3\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B[mQ\xcf\xf7a\x83\x9b \x95@\x1b\x89\x97\xab\x0fo\xe6o\x7f?\xff\xdd\f@\xb3\x06\x97\xb0a|\x1fZ\xe7\x8de[T\x86'\x92\xf3\x03*\xb4f.\xcd̵\xc8醭5\xa1]\xc2e!Qȷ'\xce\xdfEb\xebD\xec1\x13\x8b\xebJ:\xff\xe7\xe9=\x8f\xd2\xf9\xb8\xafU\xc125\xc5V\xdc\xe2v\xc6\xfa\xbf\\\xae\xae`\xe3TZ\x91z\x1b\x14\xb3\x13\xc7g\x00\x8e\x9b\x16\x97\x10O\xb7\x8c\xa3\x98\x01d\xd5Dj\x150!\xa2\xb2\x99z\xb2R{\xb4+\xa3B\xa3\xcfw\tt\xdc\xca\xd6Ge&Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6\xe0\xe1\xc0\xa4b\x1b\x85\x8b\xbfjV\xfe\x8f\xf4\x00~vF?1\xbf[\xc2<\x9d\x9a\xb7;\xe6\xcaj\xb2\xd1SgƟH\x00\xe7\xad\xd4\xdb1\x96\x1e\x99\xf3/LI\x119y\x96\r\x82t\xe0w\b\x8a9\x0f\x9e&\xe8W\xd2\x10\x90\x8a\x10\x8a\x86\xe0\xc8\\\xbe\a\xe0\x90\xa8D\x1d\x8ds\xaa\x06w]\xb1M\xac\xc0K\x8fJ\xe2\x9ff2\xf7\x1d\xb2ſ\xe7\xdc♤\xf3\xaci\xaf\xe8>lq\x8aؕ*\xdec͂\xf2]Q\xc9J\xaa\xeb\x97\xd7b\xb5\xc8\xe7\"\x9d\xba\xba\xf1\xfd\xd5\\\xbauc\x8cB\x96\xa8\xa4]\x87\xb7\xc9\v\xf9\x0e\x1b\xb6̛M\x8b\xfa\xe1\xe9\xc3\xcbo\xd7W\xd30\xe6H\xbd\xa0 ñ\x8emvh\x11^b\xfc%\xbb\xb9,ڙ&\x80\xd9\xfc\x8c\xdc_\x8c\xd8ZӢ\xf5\xb2\x04K\x1a\x1d,\xea\xcc\xf6x\xfaWu\xb5\x06@b\xa4S \b\x940\xf9U\x8e\x1f\x14Yr05\xf8\x9dt`\xb1\xb5\xe8P'\x98\xa2i\xa63\x83\xf3\x1e\xe95Z\"C\xb1\x1d\x94 ,;\xa0\xf5`\x91\x9b\xad\x96\xff8\xd3v\xe0Mvf\x8f\xceC\x8cP\xcd\x149k\xc0\x1f\x81iѣܰ\x13X\xa4;!\xe8\x0e\xbdx\xc0\xf5\xf9\xf8H\xd1 um\x96\xb0\xf3\xbeu\xcb\xc5b+}Ahn\x9a&h\xe9O\x8b\b\xb6r\x13\xbc\xb1n!\xf0\x80j\xe1\xe4\xb6b\x96\xef\xa4G\xee\x83\xc5\x05ke\x15\x05\xd1\tR\x1b\xf1\xbd͘\uebae\x1d\x84t\x1a\x11R_a\x1e\x82\xd7\xe42\x89T\x12\xf1b\x05\x9a\"\xd5}\xfe\xe3\xfa\x19\n'\xc9R\xc9(\x97\xad\x03\xbd\x14\xfb\x906\xa5\xaeѦs\xb55M\xa4\x89Z\xb4Fj\x1f\x7fp%Q{pa\xd3HOn\xf0\xf7\x80Γ\xe9\xfadW1\x8b\xc1\x06!\xb4\x11$\xfa\x1b>hX\xb1\x06Պ9\xfc\x85mEVq\x15\x19\xe1.kuss\x7fsRog\xa1\xe4\xd4\tӎ\xa2\xc1\xbaE~\x15w\x02\x9d\xb4\x14\x19\x9ey\x8c\xd1\xd5SP\x86\x8a\xe9\xa4\\\xc68H\xd0`\x9c\xa3s\x1f\x8d\xc0\xfeJ\x8f\xe5\x87\xf3\xc6+\x1e[\xb4\x8dt1\xbdBml?\xf3\xb03\x92wGA\xbc\xbe\xc1\x01P\x87f\xc8H\x05\x9f\x91\x89OZ\x9d&\x96\xfef\xa5\x1f^4aH\x1a\x89\xc5\xf5I\xf3'\xb4҈\x1b¿\xebm?\xab`g\x8ePG\xff\xd7^\x9d\b\xbb\xdcI\xf3!j\x97\xf1\xf0\xf4\xa1 x\x8a\xad\x1c\x98YWsx\xc8Amjx\x03B:*$\\$:T\x96\x0e*\x16\x1aK\xf06\xbcJ|nt-\xb7C\xa1\xbb\xb5є\xc7\xdc \xdd\xd3\xdc*\xdeD\xa8E\xde\xd1Zs\x90\x02mE\xf1!k\xc93'\xc1\xa6\fRKTb\x80M\x93Q\x16E\xb1((\xa8\x99\xbaa\xc3\xd5yc\xac\xa4\x99\xd4Ƀ/\x04\"\xd6\xd8&\xa7f\xedQ\v\xecg\x9bȍ\x89\x80\xe6P\xc0Q\xfa]BJ5\x16w\xf0\xd5أ\xb1\xc7\xd3\xd8t\x8f\xf7\xe7\x1d\xd2Δx\x11\x1cr\x8b>z\x1b*r\x1fr\xa59\xc0\xc7\xe0\"\xd6\xf6q\xa2\x8cX\xf0\x95\xd3{<\r\x15\r\xb7\x8c\x9bK\xa1\t\x96c\x11\xb5\x84ᄏ-\xd2 \xbb\x95A\xa5{\x11\xd4b\x8d\x16\xf5\xa0\x9a(\xe39\xe6(r\x1a\xf20\xack\xe4^\x1eP\x9dbN\"\xf0\xfc\x116\xc1\x83\b\x18\xad\xc6\xf8\xfeȬp\xc0M\xd32/7RI\x7f\x02\xe9&\xe83\xa5\xcc\x11E\xb686\xad?\xcd\xe1\x83v\x9ei\x8e\xee\\\a\x91ƒ+0\x9dv\xe5(\x8e\x05\x1d\xb3c\x18\x98\xc87\xc6y\xe0h\xc9\x1d\xd5\t\x8e\xd6\xe8픰#\xe9\x90z@\xab\xd1c̈\xc2pGɐc\xeb\xdd\xc2\x1c\xd0\x1e$\x1e\x17Gc\xf7Ro+b\xb0\xcaೈ\x9d\xdd\xe2\xfb\xf8\xe7[\xbc\xc0\xb4\t'\xeep\xdeu\x8c\xf5\x13\x95\xb7~\x87)E\xac\x93\x0f\x1a\vT@\x90k7\xd9w\x13\xb2\x8e\x85\xddX]\xde\x1d\xc5\xe4c\xf9c\x8f\xc3\xd4\xf1\x15P\x01\xf8R]t[5\xac\xad\xd2n\xe6M#\xf9\xac/m\xf2\xfb\xaf\xe3OiV\xa4\x16\x92Sq{\x8d\x1b\xa5\x89\x13W=͈\x1a\xfa]\xce\x14Z\x8e\xab)\x89\x9bk\x85\x1b\x1c\x7f\xea\uef74\xbe\t\xbas\xfew\xe8\xa9\xeet\xa0\x91\xea\x03f\x87z\x8e\x80ɍքT\xde\x00;\xa7\x81\x1f\\?\xff\xbd\x12=7\x81\xefqD\xf1\x03Q\xdeōE\xc7\xe9\x18\xf1\x12\x1c\xc6\xc4t\x8b\r\xb8\x1d\x11\x9c\xad\xd0\xde\xc3\xcb\xea\x816\x9eK\b\x06\xab\a\xd8\x04-\x14\x16\x8e\x8e;\xd4\xd4u\xc9\xfa4~\x17\x8d\xe7\xc7u\xd1j\xac\xber\xdfTt;.C\xcaoK\u061cF\xea\xa5;\x84l-\xd6\xf2\xcb\x1dB>ōE\xe1-\xf3;\x90\xdaI\x81\xc0Fԟ\n\xd9\tAϵѧ\x8c9\xdf`\x9e\xafaCb\xe75\xf0Pt|#~\x9e\xf2\xb6\xb3\x16\xca\xef\x9cݮ\xeb\xe4\xa98\x1e\x95\xe8p~\x94\xf9S\xaa>\xf9H\x19q\xc5\xcc\xcb\xf0\xc4W\xaa\xd8\xf244\x16\xccT3\x19kѵF\v\xea9\xef\xaba/,\xff\xef*\xd9q\xb3V\xd7(\xd7[+V\xb8\xab\x8d\x8b\xcf`\xafn\xe4\xd2\xe3`\xb7M2\x1bG\r\xf6\xa5\x97\xeb\xc9\xf8\x8b\xb4p\xa3%W\xa7\xaf\x93\x8eꗠce\x1b\xab\xaa\xf9l\xe4\xc4{l-R\x06\x13K\x92\xcdƃ\xda\x1c\xe9p\x87Z*ˌN\xf9\x9ez[\xa6E~U\xa0\xa5\x11\xcaG\xa9\x14\xd5\x00\x16\x1bCʢ\xb2\xdcR5\xc7b\xadu\xf8\xcd\xfc\xcd\xff\xafeT\xccy\xea\x00Q|ƃ\x1c>\xadݧ\xee\xc7\x01\x95\x82\x0e瘡\x1f?\x95׆\x85\xcd\xdb~\x82Z*\xaa\xff:\xd0qGu0\xf20\xfcn\xfd\xf8\x83\x8b=\x10j\xef\xe0H\x16t\x91%jzL~\xe1\t\xceS\x12\xb9i\xffn\x01\xae\r(\xa3\xb7h\xcbk\x0f\x15xɛ\x8c\x05\x81\x9er\x95\xde\x02\xdf1\xbd\xa5\xc8\x18\x83\xfc\xc8p\xe6\xbe\xcb'yϤ\x83H=\xe1\x1dw\x19\xf4Y\x8e\xb54\xaf1\xe6\xf43\xfc\x99\xffl\xd9\xcbkoO\xefSP[,\xd1_,\xa9\x9c\x14]\xf9\xcb\xd3\xfce|\xfb\xfb\xc0\xf0\xdd\xff[\xd5\xf3_}\xa9\x18|\xa1\xf8U(\xa7\xa1:\xf7f\xf1\xfc1\xedJ\xef\xb5\xf9\b\xb0\x8d\t~$\xf7w\x1c~4\xa6\xe3ǘ\xd7\xf0\x18?1\xdd*OhO\xb1\b\x0f\xd6\xc67\xdd\xf2\xd6\x18\x91b,+ݏ\xc0\x0f\xbd/aݵ\xe1w\xb2;\xe4\x1a\xcd҃ɔi;v\xcdJ\xee΄\xcd\xf9\xa5~\t\xff\xfc\xf7\xec?\x01\x00\x00\xff\xff\x03f\x86Y\xc0\x1d\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcVMo\x1b7\x10\xbd\xebW\f\xd0kwU\xa3hQ\xec\xadqr0\xda\x06\x82\x1d\xe4N\x91#-c.\xc9\xce\f\xe5\xba\x1f\xff\xbd \xb9+K\xab\x95\x93\\\xb27\x91Ù\xc7\xf7f\x1e\xd54\xcdJE\xfb\x11\x89m\xf0\x1d\xa8h\xf1/A\x9f\x7fq\xfb\xf8\v\xb76\xac\x0f7\xabG\xebM\a\xb7\x89%\f\xf7\xc8!\x91Ʒ\xb8\xb3ފ\r~5\xa0(\xa3Du+\x00\xe5}\x10\x95\x979\xff\x04\xd0\xc1\v\x05琚=\xfa\xf61mq\x9b\xac3H%\xf9T\xfa\xf0C{\xf3s\xfb\xd3\n\xc0\xab\x01;0\xe8Pp\xab\xf4c\x8a\x84\x7f&d\xe1\xf6\x80\x0e)\xb46\xac8\xa2\xce\xf9\xf7\x14R\xec\xe0e\xa3\x9e\x1fkW\xdcoK\xaa7%\xd5}MUv\x9de\xf9\xedZ\xc4\xefv\x8c\x8a.\x91rˀJ\x00[\xbfON\xd1b\xc8\n\x80u\x88\xd8\xc1\xfb\f+*\x8df\x050^\xbb\xc0l@\x19S\x88TnC\xd6\v\xd2mpi\x98\bl\xc0 k\xb2Q\nQ\x1fz,W\x84\xb0\x03\xe9\x11j9\x90\x00[\x1c\x11\x98r\x0e\xe0\x13\a\xbfQ\xd2w\xd0f\xbe\xda\x1a\x9a\x81\x8c\x01\x95\xea7\xf3ey\u0380Y\xc8\xfa\xfd5\b,J\x12O J]\x1b<\xd0\t\xbf\xe7\x00J|\x1b{\xc5\xe7\xd5\x1f\xcaƵ\xca5\xe6pS\x99\xd6=\x0e\xaa\x1bcCD\xff\xeb\xe6\xee\xe3\x8f\x0fg\xcbp\x8euAZ\xb0\fjB\x9a\x89\xab\xacA\xf0\b\x81`\b4\xb1\xca\xed1i\xa4\x10\x91\xc4N\xadU\xbf\x93\xe19Y\x9dA\xf8\xb79\xdb\x03Ȩ\xeb)0y\x8a\x90\v\x89cS\xa0\x19/Zɵ\f\x84\x91\x90\xd1\u05f9\xca\xcb\xcaC\xd8~B-\xed,\xf5\x03RN\x03܇\xe4L\x1e\xbe\x03\x92\x00\xa1\x0e{o\xff>\xe6\xe6|\xef\\\xd4))\x94\xe4\xb6\xf3\xca\xc1A\xb9\x84߃\xf2f\x96yP\xcf@\x98kB\xf2'\xf9\xca\x01\x9e\xe3\xf8#\x93h\xfd.tЋD\xee\xd6뽕\xc9Rt\x18\x86\xe4\xad<\xaf\x8b;\xd8m\x92@\xbc6x@\xb7f\xbbo\x14\xe9\xde\njI\x84k\x15mS.⋭\xb4\x83\xf9\x8eF\x13Ⳳ\x17\xddS\xbf\xe2\x02_!O\xf6\x84\xda#5U\xbd\xe2\x8b\ny)Sw\xff\xee\xe1\x03LH\xaaRU\x94\x97\xd0\v^&}2\x9b\xd6\xef\x90\xea\xb9\x1d\x85\xa1\xe4Dob\xb0^\xca\x0f\xed,z\x01N\xdb\xc1\nO\x1d\x9b\xa5\x9b\xa7\xbd-\xb6\x9b\x1d E\xa3\x04\xcd<\xe0\xceí\x1a\xd0\xdd*\xc6o\xacUV\x85\x9b,\xc2\x17\xa9u\xfa\x98̃+\xbd'\x1b\xd33pEڅ\xe1\x7f\x88\xa8\xb3\xb8\x99\xdf|\xda\ueb2ec\xb5\v\x04O\xbd\xd5\xfd4\xfc3\x9a\x8eFq\xce߲1\xe4\xef\xc5n\xe7;W/\x0fEdK8k\xd8\x06.\xbc\xfbu^\x8a\xa9~%3\xd5\xd1Gnt\"*\xcdw\xf4y\xb5t\xe8K\xb9@\xa2@\x17\xab3P\xefJP\xf9Ǡ\xacgP\xfey<\b\xd2+\x81'\xa4\r\x97\x95\x1ax\x8fO\v\xabw~CaO\xc8\xf3\x96ϛ\x9b\xca\x1e\xce߃WXZlʋE\xceVhNXd\t\xa4\xf6\xa7\xbcr\xda\x1e\x9d\xbe\x83\x7f\xfe[\xfd\x1f\x00\x00\xff\xff\xbeM\x1a\xea\xb1\n\x00\x00"), diff --git a/pkg/apis/velero/v1/backup_repository_types.go b/pkg/apis/velero/v1/backup_repository_types.go index fd30b04527..621ffcfcca 100644 --- a/pkg/apis/velero/v1/backup_repository_types.go +++ b/pkg/apis/velero/v1/backup_repository_types.go @@ -36,8 +36,9 @@ type BackupRepositorySpec struct { RepositoryType string `json:"repositoryType"` // ResticIdentifier is the full restic-compatible string for identifying - // this repository. - ResticIdentifier string `json:"resticIdentifier"` + // this repository. This field is only used when RepositoryType is "restic". + // +optional + ResticIdentifier string `json:"resticIdentifier,omitempty"` // MaintenanceFrequency is how often maintenance should be run. MaintenanceFrequency metav1.Duration `json:"maintenanceFrequency"` diff --git a/pkg/controller/backup_repository_controller.go b/pkg/controller/backup_repository_controller.go index c288f80590..cdf0b71a35 100644 --- a/pkg/controller/backup_repository_controller.go +++ b/pkg/controller/backup_repository_controller.go @@ -308,17 +308,21 @@ func (r *BackupRepoReconciler) getIdentifierByBSL(bsl *velerov1api.BackupStorage func (r *BackupRepoReconciler) initializeRepo(ctx context.Context, req *velerov1api.BackupRepository, bsl *velerov1api.BackupStorageLocation, log logrus.FieldLogger) error { log.WithField("repoConfig", r.backupRepoConfig).Info("Initializing backup repository") - // confirm the repo's BackupStorageLocation is valid - repoIdentifier, err := r.getIdentifierByBSL(bsl, req) - if err != nil { - return r.patchBackupRepository(ctx, req, func(rr *velerov1api.BackupRepository) { - rr.Status.Message = err.Error() - rr.Status.Phase = velerov1api.BackupRepositoryPhaseNotReady - - if rr.Spec.MaintenanceFrequency.Duration <= 0 { - rr.Spec.MaintenanceFrequency = metav1.Duration{Duration: r.getRepositoryMaintenanceFrequency(req)} - } - }) + var repoIdentifier string + // Only get restic identifier for restic repositories + if req.Spec.RepositoryType == "" || req.Spec.RepositoryType == velerov1api.BackupRepositoryTypeRestic { + var err error + repoIdentifier, err = r.getIdentifierByBSL(bsl, req) + if err != nil { + return r.patchBackupRepository(ctx, req, func(rr *velerov1api.BackupRepository) { + rr.Status.Message = err.Error() + rr.Status.Phase = velerov1api.BackupRepositoryPhaseNotReady + + if rr.Spec.MaintenanceFrequency.Duration <= 0 { + rr.Spec.MaintenanceFrequency = metav1.Duration{Duration: r.getRepositoryMaintenanceFrequency(req)} + } + }) + } } config, err := getBackupRepositoryConfig(ctx, r, r.backupRepoConfig, r.namespace, req.Name, req.Spec.RepositoryType, log) @@ -330,7 +334,10 @@ func (r *BackupRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 // defaulting - if the patch fails, return an error so the item is returned to the queue if err := r.patchBackupRepository(ctx, req, func(rr *velerov1api.BackupRepository) { - rr.Spec.ResticIdentifier = repoIdentifier + // Only set ResticIdentifier for restic repositories + if rr.Spec.RepositoryType == "" || rr.Spec.RepositoryType == velerov1api.BackupRepositoryTypeRestic { + rr.Spec.ResticIdentifier = repoIdentifier + } if rr.Spec.MaintenanceFrequency.Duration <= 0 { rr.Spec.MaintenanceFrequency = metav1.Duration{Duration: r.getRepositoryMaintenanceFrequency(req)} @@ -528,16 +535,19 @@ func dueForMaintenance(req *velerov1api.BackupRepository, now time.Time) bool { func (r *BackupRepoReconciler) checkNotReadyRepo(ctx context.Context, req *velerov1api.BackupRepository, bsl *velerov1api.BackupStorageLocation, log logrus.FieldLogger) (bool, error) { log.Info("Checking backup repository for readiness") - repoIdentifier, err := r.getIdentifierByBSL(bsl, req) - if err != nil { - return false, r.patchBackupRepository(ctx, req, repoNotReady(err.Error())) - } + // Only check and update restic identifier for restic repositories + if req.Spec.RepositoryType == "" || req.Spec.RepositoryType == velerov1api.BackupRepositoryTypeRestic { + repoIdentifier, err := r.getIdentifierByBSL(bsl, req) + if err != nil { + return false, r.patchBackupRepository(ctx, req, repoNotReady(err.Error())) + } - if repoIdentifier != req.Spec.ResticIdentifier { - if err := r.patchBackupRepository(ctx, req, func(rr *velerov1api.BackupRepository) { - rr.Spec.ResticIdentifier = repoIdentifier - }); err != nil { - return false, err + if repoIdentifier != req.Spec.ResticIdentifier { + if err := r.patchBackupRepository(ctx, req, func(rr *velerov1api.BackupRepository) { + rr.Spec.ResticIdentifier = repoIdentifier + }); err != nil { + return false, err + } } } @@ -546,7 +556,7 @@ func (r *BackupRepoReconciler) checkNotReadyRepo(ctx context.Context, req *veler if err := ensureRepo(req, r.repositoryManager); err != nil { return false, r.patchBackupRepository(ctx, req, repoNotReady(err.Error())) } - err = r.patchBackupRepository(ctx, req, repoReady()) + err := r.patchBackupRepository(ctx, req, repoReady()) if err != nil { return false, err } diff --git a/pkg/controller/backup_repository_controller_test.go b/pkg/controller/backup_repository_controller_test.go index 601552faab..8101d3e145 100644 --- a/pkg/controller/backup_repository_controller_test.go +++ b/pkg/controller/backup_repository_controller_test.go @@ -97,27 +97,83 @@ func TestPatchBackupRepository(t *testing.T) { } func TestCheckNotReadyRepo(t *testing.T) { - rr := mockBackupRepositoryCR() - rr.Spec.BackupStorageLocation = "default" - rr.Spec.ResticIdentifier = "fake-identifier" - rr.Spec.VolumeNamespace = "volume-ns-1" - reconciler := mockBackupRepoReconciler(t, "PrepareRepo", rr, nil) - err := reconciler.Client.Create(t.Context(), rr) - require.NoError(t, err) - location := velerov1api.BackupStorageLocation{ - Spec: velerov1api.BackupStorageLocationSpec{ - Config: map[string]string{"resticRepoPrefix": "s3:test.amazonaws.com/bucket/restic"}, - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: velerov1api.DefaultNamespace, - Name: rr.Spec.BackupStorageLocation, - }, - } + // Test for restic repository + t.Run("restic repository", func(t *testing.T) { + rr := mockBackupRepositoryCR() + rr.Spec.BackupStorageLocation = "default" + rr.Spec.ResticIdentifier = "fake-identifier" + rr.Spec.VolumeNamespace = "volume-ns-1" + rr.Spec.RepositoryType = velerov1api.BackupRepositoryTypeRestic + reconciler := mockBackupRepoReconciler(t, "PrepareRepo", rr, nil) + err := reconciler.Client.Create(t.Context(), rr) + require.NoError(t, err) + location := velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Config: map[string]string{"resticRepoPrefix": "s3:test.amazonaws.com/bucket/restic"}, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: rr.Spec.BackupStorageLocation, + }, + } + + _, err = reconciler.checkNotReadyRepo(t.Context(), rr, &location, reconciler.logger) + require.NoError(t, err) + assert.Equal(t, velerov1api.BackupRepositoryPhaseReady, rr.Status.Phase) + assert.Equal(t, "s3:test.amazonaws.com/bucket/restic/volume-ns-1", rr.Spec.ResticIdentifier) + }) + + // Test for kopia repository + t.Run("kopia repository", func(t *testing.T) { + rr := mockBackupRepositoryCR() + rr.Spec.BackupStorageLocation = "default" + rr.Spec.VolumeNamespace = "volume-ns-1" + rr.Spec.RepositoryType = velerov1api.BackupRepositoryTypeKopia + reconciler := mockBackupRepoReconciler(t, "PrepareRepo", rr, nil) + err := reconciler.Client.Create(t.Context(), rr) + require.NoError(t, err) + location := velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Config: map[string]string{"resticRepoPrefix": "s3:test.amazonaws.com/bucket/restic"}, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: rr.Spec.BackupStorageLocation, + }, + } + + _, err = reconciler.checkNotReadyRepo(t.Context(), rr, &location, reconciler.logger) + require.NoError(t, err) + assert.Equal(t, velerov1api.BackupRepositoryPhaseReady, rr.Status.Phase) + // ResticIdentifier should remain empty for kopia + assert.Empty(t, rr.Spec.ResticIdentifier) + }) + + // Test for empty repository type (defaults to restic) + t.Run("empty repository type", func(t *testing.T) { + rr := mockBackupRepositoryCR() + rr.Spec.BackupStorageLocation = "default" + rr.Spec.ResticIdentifier = "fake-identifier" + rr.Spec.VolumeNamespace = "volume-ns-1" + // Deliberately leave RepositoryType empty + reconciler := mockBackupRepoReconciler(t, "PrepareRepo", rr, nil) + err := reconciler.Client.Create(t.Context(), rr) + require.NoError(t, err) + location := velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Config: map[string]string{"resticRepoPrefix": "s3:test.amazonaws.com/bucket/restic"}, + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: rr.Spec.BackupStorageLocation, + }, + } - _, err = reconciler.checkNotReadyRepo(t.Context(), rr, &location, reconciler.logger) - require.NoError(t, err) - assert.Equal(t, velerov1api.BackupRepositoryPhaseReady, rr.Status.Phase) - assert.Equal(t, "s3:test.amazonaws.com/bucket/restic/volume-ns-1", rr.Spec.ResticIdentifier) + _, err = reconciler.checkNotReadyRepo(t.Context(), rr, &location, reconciler.logger) + require.NoError(t, err) + assert.Equal(t, velerov1api.BackupRepositoryPhaseReady, rr.Status.Phase) + assert.Equal(t, "s3:test.amazonaws.com/bucket/restic/volume-ns-1", rr.Spec.ResticIdentifier) + }) } func startMaintenanceJobFail(client.Client, context.Context, *velerov1api.BackupRepository, string, kube.PodResources, logrus.Level, *logging.FormatFlag, logrus.FieldLogger) (string, error) { @@ -1502,3 +1558,167 @@ func TestDeleteOldMaintenanceJob(t *testing.T) { }) } } + +func TestInitializeRepoWithRepositoryTypes(t *testing.T) { + scheme := runtime.NewScheme() + corev1api.AddToScheme(scheme) + velerov1api.AddToScheme(scheme) + + // Test for restic repository + t.Run("restic repository", func(t *testing.T) { + rr := mockBackupRepositoryCR() + rr.Spec.BackupStorageLocation = "default" + rr.Spec.VolumeNamespace = "volume-ns-1" + rr.Spec.RepositoryType = velerov1api.BackupRepositoryTypeRestic + + location := &velerov1api.BackupStorageLocation{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: "default", + }, + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "aws", + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ + Bucket: "test-bucket", + Prefix: "test-prefix", + }, + }, + Config: map[string]string{ + "region": "us-east-1", + }, + }, + } + + fakeClient := clientFake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(rr, location).Build() + mgr := &repomokes.Manager{} + mgr.On("PrepareRepo", rr).Return(nil) + + reconciler := NewBackupRepoReconciler( + velerov1api.DefaultNamespace, + velerotest.NewLogger(), + fakeClient, + mgr, + testMaintenanceFrequency, + "", + 3, + "", + kube.PodResources{}, + logrus.InfoLevel, + nil, + ) + + err := reconciler.initializeRepo(t.Context(), rr, location, reconciler.logger) + require.NoError(t, err) + + // Verify ResticIdentifier is set for restic + assert.NotEmpty(t, rr.Spec.ResticIdentifier) + assert.Contains(t, rr.Spec.ResticIdentifier, "volume-ns-1") + assert.Equal(t, velerov1api.BackupRepositoryPhaseReady, rr.Status.Phase) + }) + + // Test for kopia repository + t.Run("kopia repository", func(t *testing.T) { + rr := mockBackupRepositoryCR() + rr.Spec.BackupStorageLocation = "default" + rr.Spec.VolumeNamespace = "volume-ns-1" + rr.Spec.RepositoryType = velerov1api.BackupRepositoryTypeKopia + + location := &velerov1api.BackupStorageLocation{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: "default", + }, + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "aws", + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ + Bucket: "test-bucket", + Prefix: "test-prefix", + }, + }, + Config: map[string]string{ + "region": "us-east-1", + }, + }, + } + + fakeClient := clientFake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(rr, location).Build() + mgr := &repomokes.Manager{} + mgr.On("PrepareRepo", rr).Return(nil) + + reconciler := NewBackupRepoReconciler( + velerov1api.DefaultNamespace, + velerotest.NewLogger(), + fakeClient, + mgr, + testMaintenanceFrequency, + "", + 3, + "", + kube.PodResources{}, + logrus.InfoLevel, + nil, + ) + + err := reconciler.initializeRepo(t.Context(), rr, location, reconciler.logger) + require.NoError(t, err) + + // Verify ResticIdentifier is NOT set for kopia + assert.Empty(t, rr.Spec.ResticIdentifier) + assert.Equal(t, velerov1api.BackupRepositoryPhaseReady, rr.Status.Phase) + }) + + // Test for empty repository type (defaults to restic) + t.Run("empty repository type", func(t *testing.T) { + rr := mockBackupRepositoryCR() + rr.Spec.BackupStorageLocation = "default" + rr.Spec.VolumeNamespace = "volume-ns-1" + // Leave RepositoryType empty + + location := &velerov1api.BackupStorageLocation{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: "default", + }, + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "aws", + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ + Bucket: "test-bucket", + Prefix: "test-prefix", + }, + }, + Config: map[string]string{ + "region": "us-east-1", + }, + }, + } + + fakeClient := clientFake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(rr, location).Build() + mgr := &repomokes.Manager{} + mgr.On("PrepareRepo", rr).Return(nil) + + reconciler := NewBackupRepoReconciler( + velerov1api.DefaultNamespace, + velerotest.NewLogger(), + fakeClient, + mgr, + testMaintenanceFrequency, + "", + 3, + "", + kube.PodResources{}, + logrus.InfoLevel, + nil, + ) + + err := reconciler.initializeRepo(t.Context(), rr, location, reconciler.logger) + require.NoError(t, err) + + // Verify ResticIdentifier is set when type is empty (defaults to restic) + assert.NotEmpty(t, rr.Spec.ResticIdentifier) + assert.Contains(t, rr.Spec.ResticIdentifier, "volume-ns-1") + assert.Equal(t, velerov1api.BackupRepositoryPhaseReady, rr.Status.Phase) + }) +}