diff --git a/changelogs/unreleased/10098-shubham-pampattiwar b/changelogs/unreleased/10098-shubham-pampattiwar new file mode 100644 index 000000000..0c48c1631 --- /dev/null +++ b/changelogs/unreleased/10098-shubham-pampattiwar @@ -0,0 +1 @@ +Implement server default restore resource modifier diff --git a/config/crd/v1/bases/velero.io_restores.yaml b/config/crd/v1/bases/velero.io_restores.yaml index 89f4baff8..aa4e167af 100644 --- a/config/crd/v1/bases/velero.io_restores.yaml +++ b/config/crd/v1/bases/velero.io_restores.yaml @@ -467,6 +467,14 @@ spec: from. If specified, and BackupName is empty, Velero will restore from the most recent successful backup created from this schedule. type: string + skipDefaultResourceModifier: + description: |- + SkipDefaultResourceModifier controls whether the server-configured default + resource modifier is applied to this restore. + When true, the default modifier is skipped even if configured on the server. + Has no effect when a per-restore ResourceModifier is specified. + nullable: true + type: boolean uploaderConfig: description: UploaderConfig specifies the configuration for the restore. nullable: true diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index f309e5d4d..60395b71e 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -36,7 +36,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcW\xcdn\xe36\x10\xbe\xfb)\x06\xe8\xa5\x05Vr\x83\xa2E\xe1[\xeb\xec!\xd8t\x1b\xc4\xdb\xdc)j$\xb1\xa1H\x96\x1c:\x9b\xa2\x0f_\f)\xf9G\x96\x1d粺y8\x9c\x9fof\xbe\xa1\x8b\xa2X\b\xa7\x9e\xd0\ae\xcd\n\x84S\xf8\x95\xd0\xf0\xafP>\xff\x1aJe\x97ۛų2\xf5\n\xd61\x90\xed\x1f1\xd8\xe8%\xdeb\xa3\x8c\"e͢G\x12\xb5 \xb1Z\x00\bc,\t\x16\a\xfe\t \xad!o\xb5F_\xb4h\xca\xe7Xa\x15\x95\xae\xd1'\xe3\xa3\xeb\xed\x8f\xe5\xcd/\xe5\xcf\v\x00#z\\Am_\x8c\xb6\xa2\xf6\xf8O\xc4@\xa1ܢFoKe\x17\xc1\xa1dۭ\xb7ѭ`\x7f\x90\xef\x0e~s̷\x83\x99\xc7l&\x9dh\x15\xe8\xd3\xdc\xe9\xbd\x1a4\x9c\x8e^\xe8\xd3 \xd2a謧\xcf{G\x05\xf0y>R\xa6\x8dZ\xf8\x93\x9b\v\x80 \xad\xc3\x15\xa4\x8bNH\xac\x17\x00C\xf6\xc9P1$\xbe\xbdɦd\x87\xbd\xc8\x1e\x00\xacC\xf3\xdb\xc3\xdd\xd3O\x9b#1@\x8dAz\xe5(a\xf8_\xb1\x93\xc347P\x01\x04\f\xe1\x00\xd9]\x84 \f\bO\xaa\x11\x92\xa0\xf1\xb6\x87J\xc8\xe7\xe8\xc0V\x7f\xa3$\bd\xbdh\xf1\x03\x84(;\x10l%+\x1c\xf8Ҷ\x85Fi,w2\xe7\xadCOj\x04)\x7f\a\xbdv \xbd\x94\x05\x7f\x9cx\xbe\x0557\x1d\x06\xa0\x0eG\xf0\xb0\x1e\xb0\x02\xdb\x00u*\x80G\xe71\xa0\xc9m\xc8ba\x86lʉ\xe9\rz6\xc35\x8d\xba\xe6^ݢ'\xf0(mkԿ;ہ\x11c\xa7ZP\x02\xd3\x10z#4l\x85\x8e\xf8\x01\x84\xa9'\x96{\xf1\n\x1e\x13\x82\xd1\x1c\xd8K\x17\xc24\x8e?\xacGP\xa6\xb1+\xe8\x88\\X-\x97\xad\xa2q\x02\xa5\xed\xfbh\x14\xbd.\xd30\xa9*\x92\xf5aY\xe3\x16\xf52\xa8\xb6\x10^v\x8aPR\xf4\xb8\x14N\x15)\x11\x93\xa6\xb0\xec\xeb\xef\xfc0\xb3\xe1\xc8-\xbdrC\x06\xf2ʴ\a\aip\xdeQ\x1e\x1e\xa5\xdc]\xd9TNq_\x05\x161t\x8f\x1f7_`\x8c$Wjh\xb1\x9d\xea\t.c}\x18Me\x1a\xf4\xf9^jS\xb6\x89\xa6vV\x19J?\xa4Vh\bB\xaczEa\xecu.\xdd\xd4\xec:\xb1\x14T\b\xd1Ղ\xb0\x9e*\xdc\x19X\x8b\x1e\xf5Z\x04\xfcƵ⪄\x82\x8bpU\xb5\x0e\xb9w\xaa\x9c\xe1=8\x18\x99\xf3Li'\x94\xb1q(\xb9\xb0\x8c-\xdfT\x8d\x92y\xa4\x1a\xebA\xec\x19d@\xfa\x18\xa8y\x06H\xc1\t\xdf\"M\xa5\x93X\xbe$%v\xff҉c\xc2\xfa\x1e˶d\xce\tC \x99\x8f~\x98\x16\xeaR\f0\xdb賑\x8c\xfd\xcd00\xaeL(Lv\x871\x9d\xba\xe6\x0fM\xec\xe7\x1d\x14\xf0{\x8a\xf9\u07b6\x17\xcf\xd7\xd6\x10\xcf\xc5E\xa5'\xabc\x8f\x1b#\\\xe8\xec\x1b\xbaw\x84\xfd\x9f\x0e}\xde\xd0\x17U\xc7E\xbfۊ\x17\x14\xa3>\xeb\xf7\x11y\x83\xe0\xf9L\a\x85\xab\xac\\\x11ӠyU\xa2\xeb\xcd\xdd{ <\xa3\xfe\x8e\"ݙƾ\x91\xe2^qV\xef\f\r\x8c_zC\xbc\xdd\xd3\xfc\n\x19{\x9a\xaf\xe4݉\xf0)V\xe8\r\x12\x86=S\xbf(\xeaf-\x02\xbctJv\xe9b\x1a\b^\x02!X\xa9\xe6(\xf5\x8a\xf0\x99G\x94Ǚ\xa1,ҰΈ9\xf8\x13\xf1\x19\xf6;\xe7\xa0\x18\x18\xe9*\x06%A1\xbc\x83C\x93\xfe\b\xb5\x8cާ\x15\x95\xa5\xfc2\x99^\xb8\x96DG\xe6\xf9\xeb\xf1\xfe\r&\xbd\xddk\xa6ǸP&G\xe3<\x16A\xb5\xfc\x82\xe23\xe6\xd2\xc4q\xa7`\xe4\xef\xf8\x85w\f\xd4lE\xf1\xabSy\x00\xdf\b\xf1\xe3N1\x13>\x9a\xbc\xe7\xa7o\xd8d\x10\x03?\xb7@\n3\x13c\x85P\xa3F\xc2\x1a\xaa\u05fc\xb9^\x03a\x7f\x1awc}/h\x05\xbc\xff\vR3md\xa2֢Ҹ\x02\xf2\xf1\\\x97\xcd&\xee:\x11f\xc6\xf0(\xe7\a֙k\x8c\xdd0^\xec\f8\xbb_\n\xf8\x8c/3\xd2\ao%\x86\x80\xa7ct6\x93\xd9!8\x11\x06~\xa4\xd5\a(\r\x7f\x19\x06\xc9\xff\x01\x00\x00\xff\xff\xe5\x0fY\x99e\x0e\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4:K\x93\x1b\xb7\xd1\xf7\xfd\x15]\xeb\x83\xed*\ri\xf9\xfb\xe2J\xf1&\xad\xe2\x14\x13[\xd9\x12W\xba\xb8|\xc0\f\x9a\x1cxg\x00\x04\xc0\x90b\x1c\xff\xf7T\x03\x987H\xeeR\xb6\x85\xcb.\xf1h\xf4\xfb\x85ɲ\xec\x86i\xf1\x01\x8d\x15J\xae\x80i\x81\x1f\x1dJ\xfae\x17\x8f\x7f\xb5\v\xa1\x96\xfb\x977\x8fB\xf2\x15\xdc5֩\xfa\x1dZ\u0558\x02\xdf\xe0VHᄒ75:ƙc\xab\x1b\x00&\xa5r\x8c\xa6-\xfd\x04(\x94tFU\x15\x9al\x87r\xf1\xd8\xe4\x987\xa2\xe2h<\xf0\xf6\xea\xfd7\x8b\x97\xdf-\xfer\x03 Y\x8d+Њ\xefU\xd5Ԙ\xb3\xe2\xb1\xd1v\xb1\xc7\n\x8dZ\buc5\x16\x04{gT\xa3W\xd0/\x84\xb3\xf1ހ\xf3\xbd\xe2\x1f<\x98\xd7\x1e\x8c_\xa9\x84u\xffL\xad\xfe \xac\xf3;t\xd5\x18V͑\xf0\x8b\xb6Tƽ\xed/\xca@\xef\xf3\xb0\"䮩\x98\x99\x1d\xbc\x01\xb0\x85Ҹ\x02\x7fN\xb3\x02\xf9\r@$\xde\xc3ɀq\xee\xd9ɪ{#\xa4CsG\x10dw\vG[\x18\xa1\x9dg\xd7\x04s\xb0\x8e\xb9Ƃm\x8a\x12\x98\x85\xb7xX\xae\xe5\xbdQ;\x836\xa0\r\xf0\x8bU\xf2\x9e\xb9r\x05\x8b\xb0}\xa1Kf1\xae\x06\xb6o\xfcB\x9crGB\xd9:#\xe4.\x85ă\xa8\x11xc\xbc\xb8\x89\xfa\x02\xc1\x95\xc2ΰ;0K\x18\x1a\xe7\xc9N\xe3\xe2\xd7\t\xa2u\xac\xd6S\xa4\x06G\x03V\x9c9L\xe1t\xa7j]\xa1C\x0e\xf9\xd1aK\xc9V\x99\x9a\xb9\x15\b\xe9\xbe\xfb\xff\xd3\xec\x88\xfcZ\xf8\xa3o\x94\x1c\xf3\xe65\xcd\xc2`:`B\xb2ڡI2H9V}\n\"\x8e\x00\xbc\x1e\x9c\x0f\x98\x04\xb8\xc3\xf9\x8b\xa8\xacea\xb0Fy\x1dB\xa2?=\xc7f\bz\xb8\xaa\x8dPF\xb8\xe3\n^~\xf3T4\xc9>@m\xc1\x95\bQy6N\x19\xb6C\xf8A\x15A\xd1\x0e%\x9a\xa8hy\xd4\xfeR5\x15\x87\xbc\x15\f\x80u\xca$\x95Mc\xb1\b\xa7\"\xdc\x16\xecD\xe3\xc6w\xfe\x11\x06Q\x18dI\x83h\xdd\xe9\xc2\xef\x10J\xa6\xad\xe2\xd5\x0e\x9fd\x11C\x96Jű\xe3\x1f\xce\xd0\x12\x16\xb4Q\x05Z{\xc6P\t\xc6\b\x91\xb7\xfd\xc4E\x06\x95\xe8\xf7\xb4\xf84\xbaR\x8c\xa3\x01\xa7\xa0d\x92WHd0p\x86I\xbb\x8d*2\x17`{\xec\xe1\xa8Ǩ\xbc\x8f\v\xa7\xd0\t\xbb\xf6/\x83\xbb.J\xac\xd9*\xeeU\x1a\xe5\xab\xfb\xf5\x87\xffی\xa6I\x8d\x95F\xe3D\xeb\xef\xc3\x18\x84\xcd\xc1,\x8c\xc9\xfdo6Z\x03\xa0\v\xc2)\xe0\x14?\xd1z6\xc4@\x80<\xe2\x14\xd8#,\x18\xd4\x06-\x99\x96\xd7(\xb5\x05&A\xe5\xbf`\xe1\x16\x13\xd0\x1b4\x04\xa6\xb5\x85B\xc9=\x1a\a\x06\v\xb5\x93\xe2?\x1dlK\xbc\xa6K+\xe6\xd0:o\x8cF\xb2\n\xf6\xacj\xf0\x050\xc9'\x90kv\x04\x83t'4r\x00\xcf\x1f\xb0S<~T\x06AȭZA霶\xab\xe5r'\\\x9bL\x14\xaa\xae\x1b)\xdcq\xe9\xf3\x02\x917N\x19\xbb\xe4\xb8\xc7ji\xc5.c\xa6(\x85\xc3\xc25\x06\x97L\x8b\xcc\x13\"}B\xb1\xa8\xf9\x17&\xa6\x1fvt\xedL\xd0a\xf8\x1c\xe0\x19⡬\x80\x8c\x80EP\x81\xc4^\n4E\xac{\xf7\xb7\xcd\x03\xb4\x98\x04I\x05\xa1\xf4[g|i\xe5C\xdc\x14rK:O\xe7\xb6F\xd5\x1e&J\xae\x95\x90\xce\xff(*\x81ҁm\xf2Z8R\x83\x7f7h\x1d\x89n\n\xf6\xce'\\\x90\x93-\x91\a\xe0\xd3\rk\tw\xac\xc6\xea\x8eY\xfc\x93eER\xb1\x19\t\xe1I\xd2\x1a\xa6\x91\xd3́\xbd\x83\x856\t\x13\x86a\xc6;%:W\xaaB6\xe5%\x85\xbb\v4S\x00L\t\xcbG[W2\xd7\xe2F\x9bL#圷4\x94|\x968\xb4\xe2\x17\xf0\x8a720\xb8E\x83>\x1b\t\xbe_+\x1f!\x1c\x13\xb2\xf5i\xa1^\x01\xa7\x12\x98\xe5A\x89\x90\xc3\xd46\xe0\xac}\xc0\x99@\x99\xc4\xf8\xd5\xfd\xba\r\x86-\x13#\xee\xb3xw\x91?4\xb6\x02+\xee3\x87\xcbw'5\x97\xc6z\x1b\x90\xf0\x11\xc1)`\xa0\x05\x168\x8a\xc6 \xa4u\xc8x\x9c$'h0\xae\xbd\b\x9e\xfe$\x92\x10\x8a\xca\x18\xb5I&\xc0(\xf2\b\x0e\xff\xd8\xfc\xeb\xed\xf2\xef*\xd0\x01\xac\xa0\xd4\xcc\xd7z>\xdf~\xd1\xd5{\x1c\xad0ȩz\xc3Eͤآu\x8b\b\r\x8d\xfd\xe9۟\xd3\xfc\x03\xf8^\x19\xc0\x8f\x8c\xaa\xa6\x17 \x02ϻ`֪\x8d\xb0\x81\xf0\x0e\"\x1c\x84+=\xa2Z\xf1H\xe0\xc1\x93\xe0\xd8#Yr \xa1A\xa8\xc4c\xc2~¸\xf5\xd9\\\x8f\xe6\xafd=\xbf\xdd\xc2W\xc1y\xdd\xd2\xcfۀF\x97\xb6\f\r\xacG'X\x99\x11\xbb\x1d\xf6y\xffLY(\xccR\x80\xfa\x1a\x94!Z\xa5\x1a\x80\xf0\x80IN!> \x9f\xa1\xf7ӷ?\xdf\xc2Wc\x1e\x9c\xb8JH\x8e\x1f\xe1[\xf2>\x9e7Z\xf1\xaf\x17\xf0\xe0\xf5\xe0(\x1d\xfbH7\x15\xa5\xb2(A\xc9\xea\x18\x12\xe0=\x82U5\xc2\x01\xab*\v\t\"\x87\x03;\x82ڞ\xb8\xa7\x15\x11\xa9&\x03͌;\x9b$F>\x9c7\x9ay\xd6Ԏ\xa7ًϢ\x9ed\xbd\x9f-\x03y\"'|\xb9\xf0\t\x9c\x18\x96^Wp\xe2\xb1\xc9\xd1Ht\xe8\x99\xc1Ua\x89\x0f\x05jg\x97j\x8ff/\xf0\xb0<(\xf3(\xe4.#ê\xd4\xed\xd2\xf7\xc1\x96_\xf8?\xd7\x12\xee\xdbT\x9fJ\xbd\a\xf2\xf9X@\xb7\xdb\xe55\x1ch\xb3\xfb\xa7Ǯ\x93|\xd8Ąs\n\x93l\xfeP\x8a\xa2lk\xbd\x81\xb7\xad\x19\x0f\xee\x98\xc9\xe3g\xb2\x1d\xe2sc\b\xa3c\x16\x9b\xb8\x19\x93\x9c\xfe\xb7\xc2:\x9a\xbf\x86\xb1\x8d\xf8$\xe7\xf2~\xfd\xe6sZT#\xae\xf1$'j\x980>f=VY\xcdt\x16v3\xa7jQLvS\x0e\xbf\xe6$\xa4\xad@s!\xfd{7\xda\xdc&\xa8\x89j\xa0\xdb\xf3\xac\xfcӱ]\"\xe1\x1bv\xb1ϥ\x85g\xf9uY\x15\x1e\xd8\xce\x023\b\fj\xa6I#\x1e\U0005814cC3A\xe9\x02e\x04]c\x10\x98\xd6\x15\xc5\xf4\x90E$ \xc6\xfc7\xb2\x87YO\xdf)\x86$E\xd9v\xa56蜐\x9f\x919\xef'\x88\xfc\xbe\x8c\xeazv\x85\x92[\xb1\x8b\xdd\xce9\xa7dSU,\xafp\x05\xce4\xa7j\xae\xb3\x8c|\xa0-\xe7\xe9\x7f?\xd8\xdaj\xf8\x85\x06c\x9a\xaaQ\xdbqN\fʦ\x9e\xa3\x92\xc1\xa3҂%\xe6\rZ7\xb3^Z\xb8\xbd}\x8e\x8d\x05\xa5\xbc\xa6\xe4\x0eep\xaa*\x8d\x8a\x1e\x13\xf8\xb62u\xaa\xaf\xf2\x92B\x7f\x86o\xa0\xea\x9eʑ1\xdeY\xba]2\xd93\xe8.\xb7SZ\xf1\xc9\xcc\xd8\rN\x16\x03}O\xea!\xf9\x86\xf63\xbaH\xe1\x91-\xf24\x04G\xd7>\xbdQ\xda}m\x1f\x89\n;\xed\x90w\x8d\xfek$\xfej\n\xc4\xf7~\r\x8fF!j\xecJ\xff\xb1\xaf\v\xc5]\x8e\xa0\rj\x96\xec\n\x81\xef\xdc[\xdf\xc2\xfc\xd2\x06`\xc2Bc\x91\xfb\x0e\xda\xec\xee\x19\x84\xf6\x9d\x893\x87\x19\x9d\xbf\xce_\xa4\x1bS\xe1\xcdo\xf8RrU\x97j\x0ef\xceB\xd6r\xcd?ᴏ\x8d)\x8e\xf5\xe0:~\x05h\xc8}\x15JE\xf2\x96\x89\n9\xb4o\xdcτ\x92\xe3\x96R\x9c\xe0\xe3\xda>ND\xeft\xfdw^\x92\t&\xcc\x13\x9e?R\x98ӧ\xc6\v\x92\\O\xb6C\xa9\xaa(/\xd9\xd49\x1a2L\xff\xe0\t\x12\x0fT\xf7\x17%\x93\xbb\xa4\x93k\x1f\xec\x10*f\x1d\xe4\xfd'\x03)\xe2\x87/\xa6Sʆ/\x9c\xfd\xa8\xd1Z\xb6\xbb\xe4\xce\x7f\f\xbbB\xe7.\x1e\x01\x96\xabƥ\xed\xf7K\x1b]\xd0\xf3\xba\x87ɦ\xd8\xd8\xfb1W\xb6\xcen\xdbT\x95?3\x8c\x1b\xfd\xa7\x1d\x1e\xab\x1c\xd3\x19\xff\x99\xd6\xe19\x04Kf/\xb1\xea\x9e\xf6\xa4\xfcq\x17\xec\xce:d8\x13\xd8\xdf\xe2!1\xdb\xfa\xb9\xc4\xd2}t\x9e\x89\xa5ٗ\x18\xc3\xc5\xd0\x1bOq\xae]K\xc2\xec\xbesH\xac}\xef\xbdʳ\x98\x1d\xf1\xbb\xc6mv\xbd\xf5\xde\xf2\xfcg\v3\xfb\x1b\xe7\x1fL\xf2\xa1\xd8RM\x88\xfe|\xabA\x01Rl\xa4\xc5'\x01ﺜ\x02.\xac\xaeر\xa3ŗ~d\xaa\xe9\xf7\x91ޢZ\x8f\xa9\xf1T*{\xbe\xc3\xdd}-\x92\xaek\xcf\xfb\v\xb8\xe03\xfc\xba:\xed\f\x7f\x8f\x1bΤ\xe2V2mK\xe5\xd6o.\xa8Ʀ\xdb\xd8\xdac_V\xfa\xc0\xe2\x9f\xde⦨\n\tT{\xef\xf6,g1\xfex\xe8\x1a-ތ \\\x88\xfb\xf1[\xa6Ttݐ\x17 \a\xe4\x1fv\xef\xa6_p\xbc\xe8\x82\fs\xb1A\x1e\xe2Q\xaa\xab\xa0\xa4\xaf#\x94\x99\xbf\xb2\xc3\xc5@>&\xe8ό\xe1Iu\x9aMz\xcc\xf9\x00v|\xd3\x1c\xce4y\xf7ܿ\x82_\x7f\xbb\xf9_\x00\x00\x00\xff\xff\xfc¬w\xb0(\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Z͒\x1b\xb7\x11\xbe\xefSt\xad\x0f\xb6\xab4d\xa4$\xae\x14o\xd2*Nmbo\xb6DI\x17\x97\x0f\xe0\xa09\x03s\x06\x80\x01\f\xb9\xb4\xe3wO5\x80\x19\xce\x0fH.\xa9\x925\x17i\xf1\xd3\xf8\xf0u\xa3\xbb\xd1`\x96e7L\x8b\x8fh\xacPr\x01L\v|r(\xe9/;\xdb\xfc\xc3΄\x9ao_\xdel\x84\xe4\v\xb8k\xacS\xf5;\xb4\xaa19\xbeŵ\x90\xc2\t%ojt\x8c3\xc7\x167\x00LJ\xe5\x185[\xfa\x13 W\xd2\x19UUh\xb2\x02\xe5lӬpՈ\x8a\xa3\xf1\xc2ۥ\xb7\x7f\x99\xbd\xfcn\xf6\xf7\x1b\x00\xc9j\\\x80V|\xab\xaa\xa6F\x83\xd6)\x83v\xb6\xc5\n\x8d\x9a\tuc5\xe6$\xbc0\xaa\xd1\v8t\x84\xc9q\xe1\x00\xfaQ\xf1\x8f^λ \xc7wUº\xff$\xbb\x7f\x10\xd6\xf9!\xbaj\f\xab\x128|\xaf-\x95q\x0f\x87\xb52\xd0[\x13z\x84,\x9a\x8a\x99\xe9\xcc\x1b\x00\x9b+\x8d\v\xf0\x135ˑ\xdf\x00D\x06\xbc\xa0\f\x18\xe7\x9eSV=\x1a!\x1d\x9a;\x12!\xbbe8\xda\xdc\b\xed\xd3%\xb3\x18{\x03\xf9K\xdf\x11\x9bܞ0[g\x84,R(ދ\x1a\x817\xc6+\x9d\xf6\x9f#\xb8R\xd8)\xbc\x1d\xb3\x04\xd18\xbf\xf14\x18\xdfO\"\xadc\xb5\x1e\xa3\xeaM\r\xb08s\x98\x02u\xa7j]\xa1C\x0e\xab\xbd\xc3v+kej\xe6\x16 \xa4\xfb\xeeo\xc7\xf9\x88\x84\xcd\xfcԷJ\x0e\xc9yC\xad\xd0k\x0eHH[\x05\x9a$Cʱ\xeaS\x808\x12\xf0\xa67? \tr\xfb\xedg\xa1\x90\xe9\x81Z\x83+\x11ް|\xd3hX:eX\x81\xf0\x83ʃ\nw%\x1a\xf4#Va\x04\x9dm\x10\xa4;e\x92\xaaӘ\xcf\xc2\xd8(\xac\x955\xd2\xdfp\xa1\xcfb_\xb9A\x96\xb4\xaf\xd6I\xcd\xfc\b\xa1d\xda\xc8^\x17\xf8,\x03\xeb\x13)\x15\xc7\x1ek\x13\\\u00826*GkO\x18>\t\x19 y84\x9c\xa5\xa8D?\xa6\x05\xd4\xe8J1\x8e\x06\x9c\x82\x92I^aС3L\xdau\xb4\x8c\xa9\n\xdbi\xef\xf7z\b\xe5C+\xaf\xd73\xc1\x14\x86n_\x067\x98\x97X\xb3E\x1c\xab4\xca\u05cf\xf7\x1f\xff\xba\x1c4\x03Ѣ\xd18\xd1\xfa\xd1\xf0\xf5BR\xaf\x15\x86{\xfe_6\xe8\x03\xa0\x05\xc2,\xe0\x14\x9b\xd0z.\xa2\x7fE\x1e1\x05\x8e\x84\x05\x83ڠE\x19\xa2\x1553\tj\xf5\v\xe6n6\x12\xbdDCb\xc8\xed7\x15\xa7\x90\xb6E\xe3\xc0`\xae\n)~\xebd[\"\x9c\x16\xad\x98C\xeb\xfcA4\x92U\xb0eU\x83/\x80I>\x92\\\xb3=\x18\xa45\xa1\x91=y~\x82\x1d\xe3\xf8\xd1[\x93\\\xab\x05\x94\xcei\xbb\x98\xcf\v\xe1\xda@\x9d\xab\xban\xa4p\xfb\xb9\x8f\xb9b\xd58e\xec\x9c\xe3\x16\xab\xb9\x15E\xc6L^\n\x87\xb9k\fΙ\x16\x99߈\xf4\xc1zV\xf3\xafL\f\xedv\xb0\xecD\xd1\xe1\xf3\xe1\xf5\x02\xf5P\xbc\xa5\x93\xc0\xa2\xa8\xb0Ń\x16\xa8\x89\xa8{\xf7\xcf\xe5{h\x91\x04M\x05\xa5\x1c\x86Nxi\xf5Cl\n\xb9&çyk\xa3j/\x13%\xd7JH\xe7\xff\xc8+\x81ҁmV\xb5pd\x06\xbf6h\x1d\xa9n,\xf6\xce'3\xb0\xa2\x03E~\x80\x8f\a\xdcK\xb8c5Vw\xcc⟬+Ҋ\xcdH\t\xcf\xd2V?E\x1b\x0f\x0e\xf4\xf6:\xda\xfc\xea\x88j\xc7\xfem\xa91'\xcd\x12\xb94U\xacE\x8c$ke\x80M\xc6\x0f\x99J\xbb\x00\xfa\x92\x11e<\xe8\x9c\xd9\xd1\xf7&%\xa8E,{\x8e<\xc6;\x1b\x03U5\fT\xfdo\x12#\rje\x85Sf\x7f\x88\x94c\x938\xaa\x1d\xfar&s\xac\xae\xd9ޝ\x9f\tBr\xe2\x1d;\x93&g\x14\xa4z\xa0J\x16\x8a\x0e\xd9D\x1dp\xefh\x1cٹE\x97ެ<\x1aل\x84C\x8e\t\xfd\\r\xbc\xed\x95R\x15\xb21\x9bZ\xf13\x9b~T\xd1q\x18\\\xa3A\x1f\xff\x83\x9b\xd5\xca;cDŽl\xddGH\xb9\xc1\xa9\xc4>V\xe4n\x8e\xa9\xe6\xb8\x1d\u0089\x90\x94\x04\xfc\xfa\xf1\xbe\r;\xadeE\xe8\x93\xc8\xd2\xe7'i\x16\xf4\xad\x05V\xdc\a\xea\xf3k'-\x84\xbe\xfbu\x00\xe1}\xafS\xc0@\v\xccq\x10\xf7@H\xeb\x90\xf1\xd8H\xee\xc6`\xec{\x11|\xeaQ\x90\x10\xaeE1>\x92J\x80\x91\x8f\x17\x1c\xfe\xbd\xfc\xef\xc3\xfc_*\xec\x03XN\x99\x90\xbf\xab`\x8dҽ\xe8\xee+\x1c\xad0\xc8\xe9\xf6\x81\xb3\x9aI\xb1F\xebfQ\x1a\x1a\xfbӫ\x9f\xd3\xfc\x01|\xaf\f\xe0\x13\xa3\xa4\xff\x05\x88\xc0y\x176Z\xab\x116l\xbc\x93\b;\xe1J\x0fT+\x1e7\xb8\xf3[plC'&l\xa1A\xa8\xc4\x06\xd3\xec\x03\xdc\xfa\xe4\xe9\x00\xf3wr)\x7f\xdc\xc27\xc1I\xdcҟ\xb7\x01F\x97 \xf4\xbd\xce\x01\x8e+\x99\x03gDQ\xe0!ў\x18\v\x054\n\x05߂2\xb4W\xa9z\"\xbc`\xd2Sp\xc4\xc8'\xf0~z\xf5\xf3-|3\xe4\xe0\xc8RBr|\x82Wt\xc6=7Z\xf1og\xf0\xde\xdb\xc1^:\xf6D+奲(A\xc9j\x1f\xf2\xcd-\x82U5\xc2\x0e\xab*\v\xa9\x18\x87\x1dۃZ\x1fY\xa7U\x11\x99&\x03͌;\x99\x8eE\x1eN\x1f\x9ai~\xd2~\xcf;/>_y\xd6\xe9\xfdb\xb1\xfe\x99L\xf8\xc4\xfc\x13\x98\xe8_u\xae`bӬ\xd0Ht\xe8\xc9\xe0*\xb7\xc4C\x8e\xdaٹڢ\xd9\n\xdc\xcdw\xcal\x84,22\xc6,h\xdd\xce}1g\xfe\x95\xff\xe7ڍ\xfb:˧\xee\xde\v\xf9r\x14\xd0\xeav~\r\x03m\x1e\xfd\xfc\xd8u\x94\x87e\xcc\xec\xc62\xe9\xcc\xefJ\x91\x97\xed\xad\xaa\xe7mkƃ;fr\xff\x85\xce\x0e\xf1\xdc\x18B\xb4\xcfb)2c\x92\xd3\xff\xad\xb0\x8eگ!\xb6\x11\x9f\xe4\\>ܿ\xfd\x92'\xaa\x11\xd7x\x92#\xb7\x85\xf0=e\aTY\xcdt\x16F3\xa7j\x91\x8fFS\xae|\xcfIIk\x81\xe6L\xf6\xf7n0\xb8\xcd\xda\x13Yw7梴\xdbJ\xa6m\xa9\xdc\xfd\xdb38\x96\xdd\xc0\x16\xc3A\x871\xe9leё8\x99k>\x03\xcfR\xfc\x96p[ID4\xb4\xc5T\xa9B\xe4\xac\x02\xeb\xdbd,VF\x98\xad\xec)\xa0T=r\f\xb7_U\xec\xe1\xf5\xbe\xe0ḧ\x1dB\x1e\x8enQ+#\n!Yu\xf0\xd8\xfe\xea(Y\xcd\xfc_\t[\xad\x99\xd6B\x16\x17q\xdbַ\x96蜐E\"\xd1\xef\x97\xdfO]\aN\x9e\x93\xf3.\xe0\xc3\b\b0\x83\xc0hO\xa4\xaa\r\uecd0uj&(e\xa4\xac0\xa6\xd6+\x04\xa6uEy]\xc8$S\xbe\xa9\xad\xd6\xe5J\xaeE\x11+\xa7S\xa6dSUlU\xe1\x02\x9ci\x8e]ڒǽ_(<\xa3\xf1\x0f\xbd\xa1\xad\xbaϔ*ӻ\x1a\x140\xa7\x9bA\xd9\xd4S(\x19l\x94\x16,\xd1N\x87s☨\xe3\xf6\xf6\x12\x93\n'\xff\f\a\xe1Μ*8D\xc7\x11\xaf!\xf1\x8a\x1d\xdcG:\x9a_\xeaP\f\xfe\xdaНj\x880K\xd7VFc\xb4\xe27c\xd2\xfa\xbex\xd4y\xf0\xa4\xe3\x8e\xe1\xa1\x1f\xf5\x06\n\x9eU\x96\xf2\x85\xf2K\nS\xe19,\xf2\x1e\xd2\x00\xd7>\x92\xd1\x05\xe3\xea\xd2\x14\xdda\xb5C\u07bd!\\S\xb7y=\x16\xe2\vʆ\xc7C\"j\xec\x8a\x1c\xd1N̡\xec\x12B\x8c6\xa8Y\xd2\"\xc0?\nX_\x18\xfd\xda\x06i\xc2Bc\x91{\xdf:Y\xfchL\xe0\xccaF\xf3\xafs \xe9bWx\x9e\xeb\xbf\xc2\\U\xf9\x9a\x8a\x99r\xc8:\xda\xfc\xfbP\xfb0\x98\xa2\xec \xaf#,\x88C\xee\xafܠ$\xac\x99\xa8\x90C\xf7,}1\xf3\t\xd0\xd3d\xecs\x92_\xa3\xb5\xac8\xe7\xb4~\f\xa3B\xe5-N\x01\xb6R\x8d;b\x95_\xdbx\xb4.\x8a\xc9R\xf1sH\x1e\x14\xf70\xe4\xf1'\xb7)\x9a\x84Z\xfa\xcfp\x17a\xf4E\xcdsEJ\x1a\x93r5\x1d\xe4Ӿ\x06Nİ\a\xdc%Z\xdb\x13\x9c\xe8z\x8cn!\xd15\xf9=@\xbf3T\x92S9Mۗ\x94\xd9=\xb6'\xfa\xbe\xf7\xc7\xe5\"\xb6#\xbek\x1cBW\x87.U\xd5\xfa\x00\xffH.\x9bz\x85\x86T\xb1Je\xc4\xc0$\xefk.UL\xe8$\xb4a8\x88\x8a\xf5\xb0X@\xf7\xa7\xdc)\xe0\xc2\xea\x8a\xed\xbb\xcd\xf8\x1b\x1c\x1d\xe9\xf4s\xc2\xe1\\\xb5\xbe\x8a\"ϑ\xbc\xedt\xa5\xba\xfb\xd1B\xfa~z:Ӈ3پ\xef\xef~\x8c\xf0yV8\x91w\x0e\x7f\x1cr\x8d\x81,\a\x12\xce\x05\x8b\xf8c\x95\xcb}\xfcp\x99?ӽ'ٛ4z\xe4\xbc';>y\xf5[\x9aU\xf7\x1e\xbc\x80\xdf\xff\xb8\xf9\x7f\x00\x00\x00\xff\xffϡa\xa5-&\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec=Ks\xdc8sw\xff\n\x94sؤJ#ǕG\xa5ts\xb4v\xac|\xdf\xca*\xc9\xf1\x9e1d\xcf\x10\x9f@\x80\v\x80\x1a\xcf&\xf9\xef)4\x1e|\fHbF\x1a\xednjyQ\x89\x04\x1a@\xbf\xbb\xd1\xc0\xacV\xab7\xb4a\xdf@i&\xc5\x15\xa1\r\x83\xef\x06\x84\xfdO_>\xfe\x9b\xbed\xf2\xdd\xd3\xfb7\x8fL\x94W\xe4\xba\xd5F\xd6\xf7\xa0e\xab\n\xf8\x116L0äxS\x83\xa1%5\xf4\xea\r!T\bi\xa8}\xad\xed\xbf\x84\x14R\x18%9\a\xb5ڂ\xb8|lװn\x19/A!\xf00\xf4\xd3?^\xbe\xff\xd7\xcb\x7fyC\x88\xa05\\\x11\x05\xdaH\x05\xfa\xf2\t8(y\xc9\xe4\x1b\xdd@aan\x95l\x9b+\xd2}p}\xfcxn\xae\xf7\xae;\xbe\xe1L\x9b\xbf\xf4\xdf\xfe\x95i\x83_\x1a\xde*ʻ\xc1𥮤2\xb7\x1d\xc0\x15Q\xbe\xb9fb\xdbr\xaab\x877\x84\xe8B6pE\xb0}C\v(\xdf\x10\xe2\x17\x85\xfdW~=O\xef\x1d\x88\xa2\x82\x9a:\xc0\x84\xc8\x06ć\xbb\x9bo\xff\xf40xMH\t\xbaP\xac1\x88\x9a\xffY\xc5\xf7$,\x810M(\xf9\x86(\xb0\xb3A\x92\x10SQC\x144\n4\b\xa3\x89\xa9\x80Ц\xe1\xac@\x8a\x10\xb9\xe9A\n\xbd4\xd9(Yw\xd0ִxl\x1bb$\xa1\xc4P\xb5\x05C\xfeҮA\t0\xa0I\xc1[m@]F@\x8d\x92\r(\xc3\x02\xba\xdc\xd3\xe3\xaa\xde۹\x85\xd9\xc7\xe2\xc2\xf5\"\xa5e/pK\xf0\xf8\x84ң\x8f\xc8\r1\x15\xd3\xddR\xc3\xf2\b\x15D\xae\xff\x06\x85\xb9\x1c\x81~\x00e\xc1X궼\xb4\\\xf9\x04\xca\"\xab\x90[\xc1~\x8d\xb0\xb5]\xb8\x1d\x94S\x03\xda\x10&\f(A9y\xa2\xbc\x85\vBE9\x82\\\xd3=Q`\xc7$\xad\xe8\xc1\xc3\x0ez<\x8f\x9f\x90xb#\xafHeL\xa3\xaf\u07bd\xdb2\x13d\xad\x90u\xdd\nf\xf6\xefPlغ5R\xe9w%<\x01\x7f\xa7\xd9vEUQ1\x03\x85i\x15\xbc\xa3\r[\xe1B\x04\xca\xdbe]\xfe]$\xea`X\xb3\xb7<\xaa\x8dbb\xdb\xfb\x80\xa2r\x04y\xac\x109\xc6s\xa0\xdc\x12;*\xd8W\x16u\xf7\x1f\x1f\xbe\xf6\x99\x92iO\x94\x1eoN\xd1\xc7b\x93\x89\r(\xd7\x0fY\xd3\xc2\x04Q6\x92\t\x83\xff\x14\x9c\x810D\xb7\xeb\x9a\x19\xcb\x06\xbf\xb4\xa0-\xbf\xcb1\xd8k\xd4Gd\r\xa4mJj\xa0\x1c7\xb8\x11\xe4\x9a\xd6\xc0\xaf\xa9\x86W\xa6\x95\xa5\x8a^Y\"dQ\xab\xafeǍ\x1dz{\x1f\x82\xae\x9c \xad\xd7\"\x0f\r\x14\x03I\xb3\xdd\xd8&\xa8\x8b\x8dT\x03%c\xbb\fq\x94\x16~\xfb8-b\xd5\xe2\xf8\xcb\x12\x97\xd9\xe7\xdfco\xcbovf\xad`\xbf\xb4\x80\xcaԉ?\x1c\xea+\xd5S\xfa\xc3Dzј\xba\x93\x88\xb6\x0f|/x[B\x19\xf5\xfa\xc1\x02s\x96\xf1\xf1\x00\n\x9aCʄ\x15\"k\x97\xecZD\xf7\x15\x158U@\x844\txL8x\x84\t\xc4@\x92&\xd8\xd0@\x9d\x98\xf1\xec\x92\t\x11-\xe7t\xcd\xe1\x8a\x18\xd5\x1e\xa2\xd1\xf5\xa5J\xd1\xfd\x04\xb6\x82o\xf0,dE ^\xd5pV ɣBA|\xfdqQŴU\x94a\x95w\x92\xb3b\xbf\x80\xaf\x8f\xc9NAZ\xbd\xec\xfa\x15\x925T\xf4\x89I\x95\x12\x03\xa9\xb0iϞwjZZ-遌m\\悓Ȫ\xa4|\\b\x88϶Mg\x1dH\x81\xaef\\\x8a\xa7\xb6\xb7\xddk \xf0\x1d\x8a\xd6$\xa6IH٢i\x92\x8a4R\x9bi\xbaO\xab.\xd2w\x8eR\x1fg\x98\xe6`eIVw\x8fW\u0081\xa8\x16\a\x03\x85,\x05\xd8eԖ\xa8][%[\xd7v\x12)dM5\x94D\x8aɑ\x91]Z\x0eڏU\"gtz\xe8\xa2[?z<\x84\xd35p\xa2\x81Ca\xa4:Df\x0eJݓ\xa3X'P\x99ЦC\t\xe8\x160\x03\x92XN\xdfU\xac\xa8\x9c\x87a\xd9\x13\xe1\x90R\x82\xb6\xda\x04]\xe6\xfd\xd4\"\xc9\x12\xf9\xfd sڣ{\x16\xc4j\f/\xa5Q\xba'C\rwO\x12\xb5\x9d\xee=\xd0-\xfe\xbd\x91\xb3\xcb\xfe\xff\x89\xd8`LN`\xda\x19\xf9'\xe8~f\xf3\xf4$\xdfb\x84\a\xfa\x92\xdcl\bԍ\xd9_\x10f\xc2\xdb%I\xa0\x9c\xf7\xc6\xf8\x03\xd3\xe6x\xa6\xcf$M\x8eL\x9c\x890q\x88? ]\xd0d{\x84=\x82Igs\x0e\x9f\\np\xcf#$\\\xff\xd43\xc0\xa1\x9d\x93\x0f\x8b\x1d\x9e\xec\vD\x04\xc6\xf0\xb9l\xe0\x1e/\n\x89\xdcI\xfa\xc9\xd4%\xe1\t\xb8?a\x99Y\xac\xd2\x1f\xa3\x9f\xfaD\x0e\xf8A;ZZ\x89\xa9\x98\xcfij@\x99\xc9%\xa8{\xbeQ\xce\xca8\x90\x93\x91\x1bqAn\xa5\xb1\x7f0@\xd3\xc8(?Jз\xd2\xe0\x9b\xb3`\xd4M\xfc\x9c\xf8t#\xa0\xa0\t\xa7\xe5-\xc2\xfa9?g\xd3,\xb7E\xdc3Mn\x84\x8dW\x1cJ2\x87\xc2\xf4\xae\x1b\xce\rT\xb7\x1a\xd3uB\x8a\x15\xda\xcc\xe4H\x1e\xdfR\r\xd0\xfd\xecA\xfd\x80_\xad\xb1p_\\\x92\x99\xd3\x02\xca\x10Yb\xf6\x93\x1aز\"s\xbc\x1a\xd4\x16HcUx\x1eGd*V\xbf\x9a\xe3\xd8'\xcfz\xf7\x9f\xef\xabǘ/XY\x93\xb3\xf2\x10\x8c\xac3p\xe0uw\xb9\xbc\x9e\x95\x95ٌV\x81\x13\x16\x9bN$G\xa7\x9b\xe6 \xe5\x19\xe8@+\x8e.\xce\"uiY\xe2\xe6\x1a\xe5wGX\x94#x\xe1X\xd5Л\xbb3\xc15m\xacZ\xf8okiQ\x9a\xfe\x974\x94)}I>\xe0N\x19\x87\xc17\x9f\x87\xeb\x81\xc9\x18\xb2\xb1CY\xfey\xa2\xdc\xda~\xab\xc0\x05\x01\xee<\x01\xb99\xf0\x8b.Ȯ\x92ڙ\xed\r\x03\x8e\xfb\x15o\x1fa\xff\xf6\xc2\x0e\xbf8d_ɼ\xbd\x11o\x9d\x0fq\xa00\xa2\xc3!\x05ߓ\xb7\xf8\xed\xeds\\\xa9LN\xcdl6`њ6y\x1c*\x92\xc9\xfa\xee\x19pL?7\xdf%当=\xb7\xda,\x16m\xa46\x9f\xd3yÉ\xf9܅\x1eC\xcf8\x91c[\x8c\x18|\x1e-\xea{\xebDn\f(\x9fKt6 \xc4\x1fό\xccR\xbb2\xfd\xc9\xc6d \x8d\xf9]\x8b\xe0\x05nr\x1b79S<\xc6a\xb5x9\xd2\xdb\xff\xf8\xbd\x97ϴ\x92k\xff\xef/\xe4\xa5\x1d\xeaB\xd65\x1d\xefjfM\xf5\xda\xf5\f<\xed\x019\xea\xabm\x8b\xf2\x9ck\x91;\x1e\xc2\xfd\xcb\x1d3\x15\x13\x84\x06\xb5\x01\xca3\x14%\x8dL\xe5\xb0SOE5Y\x03\x88\x98\xa2\xff=\xb8\x125\x1378\x00y\x7f\x06\xd7#\xa2\xeb\x9c\xce\xeeu\xa4I\xa4||\xe1LV#K\xb2\xab@\xc1\x801\x0e\xf3\xee\xe8\xa9\niz)\x8b#\x1c\xd2F\x96?h\xb2aJ\x9b\xfe\x144iu.\xad\x8f$\x9f\x9d\xf7WV\x83l\xcd9\x11\xfc\xb1\x1bf\xb0\xd7\\\xd3\xef\xacnkBk\xd9:cnX\x1dwu=zw\x94\x99\xb8m\x85\xf9\x1b#-\t\x1a\x0e\x06\xc8\x1a6\xe9\xfd\xde\xd4SH\xa1Y\t*T)8\xb21i\x05sC\x19oS\xbbD\xa9\xe7\xd8\bX|T\xea\xa4\x00\xf8\x8b\xeb\xd9\xcb;Vr7DP\xe6\xdaq#\r\b\xdb\x10f\b\x88\xc2b\x1c\x94S\xc98\x84G\x06\xa2\x86\xe5\xea\xb9<\x05n\x1f\x10m\x9d\x87\x80\x15\n$\x13\xb3)\xb7~\xf3O\x94\xf1s\x90\xcdr\xde'\xa9\ue056\xa7\xe4h~\xeeu' t\xabp\xf3\xdf\xe9\x8e\x1d\xe3ys\xb6\x94#\x9c\xb6\xa2\xa8\x00\x95\x90\x18\xea\x06\a\x9e\tm\x80\xe6\xf2\x82\xf5\x8aZ!\x98\xd8\xe6\xd1.;\x11\xda=\x0e\xd5k)9\xd0\xe9]\xc8\uec78~\x05M\xf4s7\xcc35QG\x04\xb7m\x8etȦ\xa8UZ\x84\x1a\x03u\xe3DN\x12Պ\xbeu9\x83\":&\f\xf7\xb3x\xc9\xf8\x9a\t\x96A\xdb\x01]o\x043}\xe7т8\xab\xf3h\a\x88\xee\xc0)\x19\xb6\x9b\x01\x00+\xa0!\x0e\xc1\xb9G\xae9\u0091\\\x03\xa1e\t\xa5\xcb]ZWć%\xae\xf0m\xa2\xb8!\xb9\xba\xe3=\xc1,ʆg\x10tb\x1eV=\xc1\xaa\x15\x8fB\xee\xc4\n\x83q}\xb4\x0e91K\xf5\xdc\xe1\xcd\xc9\xcahY\xbf\xe4\xab\xe9%-4\xe4\xd7|\x9e\n\xfe\xd3\x19\xb4L6\xdf\x1c\x95\xf0\x98\xe3\x82%\xbd\xe6\n\xb0'>.\xcebn\xfc\x99\xce~S\xfa\xda\x15K?\xab,\xee&\r\xaa\xe7\x14\xee*0\x15\xa8P\x9a\xbd\u0092\xf4rv\x87\xb4\v^b\x9d\x9ce\xaa\xe0\"\xbb\xf2\xcfQ\xe5\x1cF7-\xe7\x17\x96\xb7i˓ᰑ(b\x87\x9c\x95U?\x96\xf6\x18r\xaa/\xb2\xf1د\xb4\x18\xd6\x17\xc6*\x88P`(\xc3ȞƩ\xf5baio\x7f\x7fXN\x81\xf9\xbf0\xfd\u07fc\xf40\xa3R\"\x1f\x8d\xb9U\x9a\x11\x89\tX\t\x06롱\xab\xaf\xf0\xed|\xa1\xef\xef\v\xa7\x06\xea/\x8d\x97\x98I\x176\x03\xad\t8\xa3z\x13\xb4\x06\xadv\xae@\xb4\x03>gh\xfb\x7f(\xdc)\x88\x00&ů_+\b\xe2\xeb\xab\xf7\x99&\xffL*\xd9&\xaa\xfafP\xb6Pݱ\xbc\xe0A\xa1\x87\xdfP\x00C\x9f\xde_\x0e\xbf\x18\xe9\xcb>0\x8b\x96\x00\x84AQ\x97\x99e\xa2dO\xacl)\x0fR\u06dd!p\f\xd4\xf1Y\x02\x9aTD0\xee\x180\xf4\x1f0\x1c\xf9Ҹm\x99\xa3Uܼ/\x9aW\x1drrMȰ\xe6c\xc2\x1a\x1e\xbb}\xf1\"U\xb0\xbfI\xad\xc7\xf1\x15\x1e9\x91\xc4B5\xc7\t5\x1c\x99\xc5b\xcf\xdeoɩ\xd28&\xe6>[E\xc6\xcb\xd7ad\xe1g\xb9\xe6\xe2\x18윽\xbe\xe2\x15\xab*^\xa7\x96\"\xb3\x82\xe2\xe5J!\xf3\xa2ϓJ\x01\x96\x03\x96\xe9*\x88\xc5ڇg\x054'-i\xb1\xa6\xe1\x98J\x86E\xea\xe4\x89٫\xd5*\xbcZ\x85\xc2\xeb\xd6%\xccr\xd1\xec\xc7c*\x0fb\x9c\xf4\x13m\x1a&\xb6\x87L\x91\xcb:\xb3l\xb3\xcc2\xb7\xa3\x89\fx\xa6\x1f\xcet\xd1\xe1D\xe8\xeb\x8eK'\"ɐ\xb6d\xc2\xc8K\xf2A\xec=\xdc\x04\x9c^\xf8(\xa498\xc8f\xa7\xb5c\x9c\xf7Ok!\xd8yP\xfe̤\xa6\xb5\x9bՔ\xb7\x9f\xa4\xabT\x03\xa7\xfc\xa4\xc0\xf1\xcb\bF?;\xfa\x9a\x9e\x7f\xddr\xc3\x1a\x0e֣{be\xf2\f\x99\xa9`\x1f\x91\xfc7\x89'\xa4\xd6{\x84\xf4\xe5>\xca\xe2\xe5(\x88\xa1\x9a\xec\x80sBS\xdcq\xb0\xfc\u009dL.\xe4\n\x8f\x04Z\xf2\x06&\xf1\xe7\x99/\x9c\x14\xe310\xa4^\x9d\x80[P\x81\xa7\x9bub!\x93\xe60G\x8b\x1e\xf8\xe5.\xba\xc0w\xbf\xb4\xa0\xf6D>a\t\x83\xf7\u07ba\xb3\n^\xddh\x1bc\x06\x05\xe8\x95\xf1Ԧ\xc2A(\xd3)(\xf2A8_b<\x1f\xecc5_\x17\xaaYun\xa3\xb0\xe4\x18\x13݅\x8c\xbd\x13ݖ\xdc\xfeܢ\xfe\xf3\x06nLJn\x8b\xbeR\xbe?\xfb\x1b\x15\xeb\x9fR\xa4\x9f\xb7\x1d\xb4X\x94\x7f\xae@n)\x94\xcb\xf6^\xf3\x8a\xee\x8f\xdbD=c\x91\xfd9\x8a\xeb31\x95SL\x7f\x1c\x9e^\xa1x\xfeU\x8b\xe6_\xabX>\xbbH>k\x1f3{\xd3*w\x9b\xf1Ī\xef\xe5]\xf7\xf9\xa2\xf7\x8cb\xf7\x8c\x9d\xb4\xe5E\x9e\xb0\xbc\x8cb\xf6\xe3\x8a\xd83h\x96+\x8a\xafX\xac\xfe\x8aE\xea\xaf]\x9c\xbe\xc0Y\v\x9f\x8f+B?y\a&l\xf5\xdf\xca\x12\xee\xa42K\xc1\xc9ݸ}b'\xb5\x17\xb0I^\x12\x11\x9a&V\x89!\x86\x0f/N[Tz\xd33\xb8\xd3?\xc9\xd2\xcemi\x8f\xe5~\xd4\xfc\xe0\xac\xf2\x06\x14\bw\xcd\xc7\x7f>|\xb9\x8d\xf0S>\xaf\xf7\x8cG\xd7K8\x0f\xa6\xf4\xc8\xf1[s\xbe\x98\xc9a\v}\x80\x17\xde\x17\xa1\r\xfb\x0f\xbc\xef\xed\x19\xe9\xa0\x0fw7\b#\xf8ix\x81\\\xac\xa2\x88;\x96k\xb0\x16+\xa2jR,n6\x03\x88Ê\xdf\xfe5JP\xba+\xb3\x82\xc5d\xa1\xc6\xcb\n\xdeݍ\x9b\xc7\xd4(\x9f\xac\xd3(\xf6D:\x8e\xac\x98*W\rUf\x8fl\xa3/\x06s\bff.\x9d3\xa9X\x0f\xaf\x01K\xa27\xdc\xfe\x85{\x91\xfbf\xb8\xdb;\xc6\xdd)\xf3\x98>\x7f\xb2x\xf2\xe4\x05\xe71m\xb1W\x88\xa9\xc4\xebd\x81ɋ\xa5\xc9\xd417\x05%e`\xe1ڠ\x9ej\xa0\xe4Z\x8a\r\xdb\xfeD\x9b`F\x1c>'\x95\x85O\xd14\x16\xb4\x05養u\xb5ihwzPi,a%t.eU~B\xc8w\x01\xb0\x06\xb7\xbd\xed\xb4R\\B\x03j\xd5\xe5ۺی\xf6\xcd\xf4l\xf5\xc5(d\xf5\xb7\xdc\fj\x17\xac\x1a4\xa0\x84\xff\x96\x9a\xab/\xb8y\xc0z\x9b\xdet\xf7q\xb2\x16\x1bv\x86\x96q\xfc\xe0x9\xd1fT\xac\x93\x00>ʧt\x18\xdcHUS\x13$\x00\x13z\xd4\xe1\xddݚ\xf6\xd0@q9$\xf9\x9f:\xf9O\x9d\xfc\xa7N~Y\x9dl\x95\xdbݷ\x93R\xe1\xf7\xb1\xf7\xbc\xefI9\x8f\xe9\xff\x04\x18\xdb\x1f\xddO-h\xa3\xab\xc45x\xcf\xf3?\xf1\x86HCM\xfb\x9cE:\x00\x83u\xb2\xa2\xeay\x90;\b>fX6J+vKjp\xe0\xfe\xa4\x15\xe3\x17\xbd\xec\xed\xeb\x94\xe9d^\xb1u\xf2\xe5Z\x0e=\x13\xea\aw$\xacj;\xc4\xd4\t\x05:\x8b\xe1v\xc6\xc1\x8f\xf9\xc4B\xe6\xd5Ly\x06\xe3\x84\xeb\x98\x10_\xb9\xb8\"\xc9[\x9a2ob\xfaM\x11=\xa3\xd5tQA\xd9r8\xf5\x1eև^\xff\xe5\x9bX\xc3h\x19w\xb1Zd\xf7\f\xb4\xf5\xb0\x86w\xbezJx\xc8}JN\x05ᘰqW>\x16\xeev\xe0\xa2\x00\xad7-\x0f\x95\xa3\x85\x02j\xa0\f͙\x8e3>\xaa\xf6\xb1m\xb8\xa4%(\xe7\x92-\xa0\xf5\xbf\x06\x8dG<[\xe0\xcbVu\xd7\xed\xce^U\xfa,\xcd\xd5PE9\a\xfe\x89q\xd0?ʝ\xb0\xf3\xca\x10ȻT\xbf\xdeY٢U֬\xef\x89h\xeb5(\xa2\xc1\x98\xe9\x04\xdeF\xaa\xf9S+\x0e\xefL\x18\xd8B*\xe7\xb9S\xcc\xc0CC\x95\x06\x9cQ\xc6\n~\x1euq\x19\xc1\r\xa7[W\x9e\\\xb2\x82\x1a\x88\x06\x18G\x98\x9a>\xf6\xd7\b\x8b\xef\xb1ZTNlDd\v\xf5\xd41\xb9I\xb1\x9e\xba\xf29a\xaa\x93\x97>;\x8b\\\xd0\xc6\xe0\xa1D\xa4#\x12\xd1x\x18x\x91\xfa\xe8\xde\xe7\x01\xd8iN\xf3GK|\x11\xb36\xb4ND\t\xcbz\xe7\xfa\x10\f^ծ\xca^-t\xff\xd2\xdbX\xf4LvT\xc7\x03.I\u07fb\x83\xed\xc0\xa0\xabnACI\xe0\t\x04\xb1\xa2H\x19\x87r\x8eS\xbf\xe2\xe6\x9ez\x02\xf5\x83\x8ep\xb0:۲\xf8\x83\xa1\xcaĩ\x1f\xfa1.\x86\xbb\"%5\xb0\xb2\xbdOs\xdd\xd2WW+ub\x89\x06\x9e6\xf6\xe2Q\x84\xa3\x90\xd6\xfa\xb93\xc25hM\xb7!1\xb8\x03\x05d\v\xc2\xe2=\xee\xf7$=\xa6p\xcc\xda\x1b\x8bAb\x80\x16\xa6\xa5~\x00\xe7\xc2Ŋ\x96pg\x10\x9cC\x1d^1\xe2\f:\x9f\xaa3\x18\xdc`D\xb4\xc5\xde)ʄ85v3\x1dv癚\xaf\x11ʔz\xf4\xeb\x1b\xfc8\x82/z\xf1\x8d,ي\x8a\x8a\xed\xe4!\xe3J\xc9v[\x05ޜr\x88H\xd9b\xe4ܠ*\xd0\xe1ǜL\xabD\xaf\x90\xc2\u05fdMi\xe98\xddi\x1f\xe5\x19\x8aZu\x87\r;U5c\U000f3cc4\x13\x10\x17m\x7f\x02\"\xd5{Q\xcc\x1e\x8b<ܣ:ʵL\"!j\xe3\x17CB\x848\x85\x84\xbe/\xd1E<\xbf\x1b\x8cL\xf9('\xa2cމ\xc1%\u0383Z^t\xdf\t\x1a\xba;ǡC\x0f\x82\xbf\x93\xd2n\x03\b\xc7D\xbe8v:\xee\xfd\xfdF\xacO\xd1\xdb\xfaxr\xec\xfam\x04ct,\xddF\xb1\xdd0!\xde\xfc{\xb6Iɋ\xfbż5\x87\x7f8\xf8\xfa\xca\xc7\xcbwT\t&\xb6'a\xe4g\xdf7\x11\xcf{\xb0\xe7\x8c\xe8\xc3\xcc_,\xa6O\x9a\xa5\x83\x97\xc8\xe0e\x0f\xcf~$\xff\xe6\xff\x02\x00\x00\xff\xffJ\xb7g~\xf1r\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec=Ks\xdc8sw\xff\n\x94sؤJ#ǕG\xa5tSd;V\xbeoe\x95\xe4\xd8g\f\xd93\xc4'\x10\xe0\x02\xa0ƳI\xfe{\n\x8d\a\x1f\x03\x92\x98\xd1cwSˋJ$\xd0\x00\xfaݍ\x06f\xb5Z\xbd\xa1\r\xfb\x06J3).\bm\x18\xfc0 \xec\x7f\xfa\xfc\xe1\xdf\xf49\x93\xef\x1e߿y`\xa2\xbc W\xad6\xb2\xbe\x03-[U\xc0\a\xd80\xc1\f\x93\xe2M\r\x86\x96\xd4Ћ7\x84P!\xa4\xa1\xf6\xb5\xb6\xff\x12RHa\x94\xe4\x1c\xd4j\v\xe2\xfc\xa1]úe\xbc\x04\x85\xc0\xc3Џ\xffx\xfe\xfe_\xcf\xff\xe5\r!\x82\xd6pA\x14h#\x15\xe8\xf3G\xe0\xa0\xe49\x93ot\x03\x85\x85\xb9U\xb2m.H\xf7\xc1\xf5\xf1㹹\u07b9\xee\xf8\x863m\xfe\xd2\x7f\xfbW\xa6\r~ix\xab(\xef\x06×\xba\x92\xca\xdct\x00WD\xf9暉m˩\x8a\x1d\xde\x10\xa2\v\xd9\xc0\x05\xc1\xf6\r-\xa0|C\x88_\x14\xf6_\xf9\xf5<\xbew \x8a\nj\xea\x00\x13\"\x1b\x10\x97\xb7\xd7\xdf\xfe\xe9~\xf0\x9a\x90\x12t\xa1Xc\x105\xff\xb3\x8a\xefIX\x02a\x9aP\xf2\rQ`g\x83$!\xa6\xa2\x86(h\x14h\x10F\x13S\x01\xa1M\xc3Y\x81\x14!rӃ\x14zi\xb2Q\xb2\ue82di\xf1\xd06\xc4HB\x89\xa1j\v\x86\xfc\xa5]\x83\x12`@\x93\x82\xb7ڀ:\x8f\x80\x1a%\x1bP\x86\x05t\xb9\xa7\xc7U\xbd\xb7s\v\xb3\x8fŅ\xebEJ\xcb^\xe0\x96\xe0\xf1\t\xa5G\x1f\x91\x1bb*\xa6\xbb\xa5\x86\xe5\x11*\x88\\\xff\r\ns>\x02}\x0fʂ\xb1\xd4myi\xb9\xf2\x11\x94EV!\xb7\x82\xfd\x1aak\xbbp;(\xa7\x06\xb4!L\x18P\x82r\xf2Hy\vg\x84\x8ar\x04\xb9\xa6{\xa2\xc0\x8eIZу\x87\x1d\xf4x\x1e?#\xf1\xc4F^\x90ʘF_\xbc{\xb7e&\xc8Z!\xeb\xba\x15\xcc\xecߡذuk\xa4\xd2\xefJx\x04\xfeN\xb3튪\xa2b\x06\n\xd3*xG\x1b\xb6\u0085\b\x94\xb7\xf3\xba\xfc\xbbH\xd4\xc1\xb0foyT\x1b\xc5Ķ\xf7\x01E\xe5\b\xf2X!r\x8c\xe7@\xb9%vT\xb0\xaf,\xea\xee>\xde\x7f\xed3%Ӟ(=ޜ\xa2\x8f\xc5&\x13\x1bP\xae\x1f\xb2\xa6\x85\t\xa2l$\x13\x06\xff)8\x03a\x88n\xd753\x96\r~iA[~\x97c\xb0W\xa8\x8f\xc8\x1aH۔\xd4@9np-\xc8\x15\xad\x81_Q\r\xafL+K\x15\xbd\xb2DȢV_ˎ\x1b;\xf4\xf6>\x04]9AZ\xafE\xee\x1b(\x06\x92f\xbb\xb1MP\x17\x1b\xa9\x06J\xc6v\x19\xe2(-\xfc\xf6qZĪ\xc5\xf1\x97%.\xb3Ͽ\xc7ޖ\xdf\xec\xccZ\xc1~i\x01\x95\xa9\x13\x7f8\xd4W\xaa\xa7\xf4\x87\x8fe\xa31u'\x11m\x1f\xf8Q\xf0\xb6\x842\xea\xf5\x83\x05\xe6,\xe3\xe3\x01\x144\x87\x94\t+D\xd6.ٵ\x88\xee+*p\xaa\x80\bi\x12\xf0\x98p\xf0\b\x13\x88\x81$M\xb0\xa1\x81:1\xe3\xd9%\x13\"Z\xce\xe9\x9a\xc3\x051\xaa=D\xa3\xebK\x95\xa2\xfb\tl\x05\xdf\xe0IȊ@\xbc\xaa\xe1\xac@\x92G\x85\x82\xf8\xfa㢊i\xab(\xc3*o%g\xc5~\x01_\x1f\x93\x9d\x82\xb4z\xd9\xf5+$k\xa8\xe8#\x93*%\x06RaӞ=\xefԴ\xb4Z\xd2\x03\x19۸\xcc\x05'\x91UI\xf9\xb0\xc4\x10\x9fm\x9b\xce:\x90\x02]\u0378\x14Omo\xbb\xd7@\xe0\a\x14\xadIL\x93\x90\xb2E\xd3$\x15i\xa46\xd3t\x9fV]\xa4\xef\x1c\xa5>\xce0\xcd\xc1ʒ\xac\xee\x1e\xaf\x84\x03Q-\x0e\x06\nY\n\xb0˨-Q\xbb\xb6J\xb6\xae\xed$RȚj(\x89\x14\x93##\xbb\xb4\x1c\xb4\x1f\xabD\xce\xe8\xf4\xd0Y\xb7~\xf4x\b\xa7k\xe0D\x03\x87\xc2Hu\x88\xcc\x1c\x94\xba'G\xb1N\xa02\xa1M\x87\x12\xd0-`\x06$\xb1\x9c\xbe\xabXQ9\x0fò'\xc2!\xa5\x04m\xb5\t\xba\xcc\xfb\xa9E\x92%\xf2\xfbA\xe6\xb4G\xf7,\x88\xd5\x18^J\xa3tO\x86\x1a\xee\x9e$j;\xdd{\xa0[\xfc{#g\x97\xfd\xff\x13\xb1\xc1\x98\x9c\xc0\xb43\xf2O\xd0\xfd\xcc\xe6\xe9I\xbe\xc5\b\x0f\xf49\xb9\xde\x10\xa8\x1b\xb3?#̄\xb7K\x92@9\xef\x8d\xf1\a\xa6\xcd\xf1L\x9fI\x9a\x1c\x99x!\xc2\xc4!\xfe\x80tA\x93q\xef-F6M\xfe\xda\xefuF\xd8&\"\xbd<#\x1b\xc6\r\xa8\x11\xf6OR\xf5\x812ρ\x8c\x1c\xabG0O`\x8a\xea\xe3\x0f\xeb\xe2\xe8.=\x96\x89\x97qg\xe7\x1b\x87\bbh\x9e\x17\xe0\x12\x8c\x97\x99\x82\x1a\xe3p\xf2\x15\xb1ٽA\xa7\xfa\xf2\xe6\xc3a\xac<~28\xef`!\vB\xe7\x9e\xcbъ\xfa\xf3\xf3QA\xf8\x82>P\f\xaa\\\xce\xe5\x8cP\xf2\x00{\xe7\xbaPA,}hh\x9c1\xbc\x02L\xfe \x9f=\xc0\x1e\xc1\xa4\xb39\x87O.7\xb8\xe7\x01\x12\xae\x7f\xea\x19\xe0\xd0\xceɇ\xc5\x0eO\xf6\x05\"\x02c\xf8\\6p\x8f\x17\x85D\xee$\xfdd\xea\x92\xf0\x04ܟ\xb0\xcc,V\xe9\x8f\xd1O}\"\a\xfc\xa4\x1d-\xad\xc4T\xcc\xe745\xa0\xcc\xe4\x12\xd4=\xdf(ge\x1c\xc8\xc9ȵ8#7\xd2\xd8?\x18\xa0id\x94\x0f\x12\xf4\x8d4\xf8\xe6E0\xea&\xfe\x92\xf8t#\xa0\xa0\t\xa7\xe5-\xc2\xfa9?g\xd3,\xb7E\xdc3M\xae\x85\x8dW\x1cJ2\x87\xc2\xf4\xae\x1b\xce\rT\xb7\x1a\xd3uB\x8a\x15\xda\xcc\xe4H\x1e\xdfR\r\xd0\xfd\xe4A\xfd\x80_\xad\xb1p_\\\x92\x99\xd3\x02\xca\x10Yb\xf6\x93\x1aز\"s\xbc\x1a\xd4\x16HcUx\x1eGd*V\xbf\x9a\xe3\xd8'\xcfz\xf7\x9f\x1f\xab\x87\x98/XY\x93\xb3\xf2\x10\x8c\xac3p\xe0uw\xb9\xbc\x9e\x95\x95ٌV\x81\x13\x16\x9bN$G\xa7\x9b\xe6 \xe5\t\xe8@+\x8e.\xce\"uiY\xe2\xe6\x1a\xe5\xb7GX\x94#x\xe1X\xd5Л\xbb3\xc15m\xacZ\xf8okiQ\x9a\xfe\x974\x94)}N.q\xa7\x8c\xc3\xe0\x9b\xcf\xc3\xf5\xc0d\f\xd9ء,\xff\x8f\x16\xf5\xbdu\"7\x06\x94\xcf%:\x1b\x10\xe2\x8f'Ff\xa9]\x99\xfedc2\x90\xc6\xfc\xaeE\xf0\x027\xb9\x8d\x9b\x9c)\x1e\xe3\xb0Z\xbc\x1c\xe9\xed\x7f\xfc\xd1\xcbgZɵ\xff\xf7\x17\xf2\xdc\x0eu!뚎w5\xb3\xa6z\xe5z\x06\x9e\xf6\x80\x1c\xf5նEyε\xc8\x1d\x0f\xe1\xfe厙\x8a\tB\x83\xda\x00\xe5\x19\x8a\x92F\xa6rة\xa7\xa2\x9a\xac\x01DL\xd1\xff\x1e\\\x89\x9a\x89k\x1c\x80\xbc\x7f\x01\xd7#\xa2\xeb%\x9dݫH\x93H\xf9\xf8\u0099\xacF\x96dW\x81\x82\x01c\x1c\xe6\xdd\xd1S\x15\xd2\xf4R\x16G8\xa4\x8d,\x7f\xd2dÔ6\xfd)h\xd2\xea\\Z\x1fI>;ﯬ\x06ٚ\x97D\xf0\xc7n\x98\xc1^sM\x7f\xb0\xba\xad\t\xade댹au\xdc\xd5\xf5\xe8\xddQf\xe2\xb6\x15\xe6o\x8c\xb4$h8\x18 kؤ\xf7{SO!\x85f%\xa8P\xa5\xe0\xc8Ƥ\x15\xcc\re\xbcM\xed\x12\xa5\x9ec#`\xf1Q\xa9\x93\x02\xe0/\xaeg/\xefX\xc9\xdd\x10A\x99kǍ4 lC\x98! \n\x8bqPN%\xe3\x10\x1e\x19\x88\x1a\x96\xab\xe7\xf2\x14\xb8}@\xb4u\x1e\x02V(\x90L̦\xdc\xfa\xcd?Q\xc6_\x82l\x96\xf3>Iu\a\xb4<%G\xf3\xbdם\x80Э\xc2\xcd\x7f\xa7;v\x8c\xe7\xcd\xd9R\x8epڊ\xa2\x02TBb\xa8\x1b\x1cx&\xb4\x01\x9a\xcb\v\xd6+j\x85`b\x9bG\xbb\xecDh\xf78T\xaf\xa5\xe4@\xa7w!\xbb\xc7\xe2\xfa\x154\xd1\xf7n\x98'j\xa2\x8e\bn\xdb\x1c\xe9\x90MQ\xab\xb4\b5\x06\xeaƉ\x9c$\xaa\x15}\xeb\xf2\x02\x8a\xe8\x980\xdc\xcf\xe29\xe3k&X\x06m\at\xbd\x16\xcc\xf4\x9dG\v\xe2E\x9dG;@t\aNɰ]\x0f\x00X\x01\rq\b\xce=r\xcd\x11\x8e\xe4\x1a\b-K(]\xeeҺ\">,q\x85o\x13\xc5\r\xc9\xd5\x1d\xef\tfQ6<\x83\xa0\x13\xf3\xb0\xea\x11V\xadx\x10r'V\x18\x8c\xeb\xa3uȉY\xaa\xa7\x0eoNVF\xcb\xfa%_M/i\xa1!\xbf\xe6\xf3T\xf0\x9f^@\xcbd\xf3\xcdQ\t\x8f9.X\xd2k\xae\x00{\xe2\xe3\xe2,\xe6Ɵ\xe9\xec7\xa5\xaf\\\xb1\xf4\x93\xca\xe2\xaeӠzN\xe1\xae\x02S\x81\n\xa5\xd9+,I/gwH\xbb\xe0%\xd6\xc9Y\xa6\n.\xb2+\xff\x1cU\xceat\xd3r~fy\x9b\xb6<\x19\x0e\x1b\x89\"v\xc8YY\xf5ci\x8f!\xa7\xfa\"\x1b\x8f\xfdJ\x8ba}a\xac\x82\b\x05\x862\x8c\xeci\x9cZ/\x16\x96\xf6\xf6\xf7\x87\xe5\x14\x98\xff\v\xd3\xff\xcdK\x0f3*%\xf2ј[\xa5\x19\x91\x98\x80\x95`\xb0\x1e\x1a\xbb\xfa\n\xdf\xce\x17\xfa\xfe\xbepj\xa0\xfe\xd2x\x89\x99ta3К\x803\xaa7Ak\xd0j\xe7\nD;\xe0s\x86\xb6\xffe\xe1NA\x040)~\xfdZA\x10__\xbd\xcf4\xf9gR\xc96Q\xd57\x83\xb2\x85\xea\x8e\xe5\x05\x0f\n=\xfc\x86\x02\x18\xfa\xf8\xfe|\xf8\xc5H_\xf6\x81Y\xb4\x04 \f\x8a\xba\xcc,\x13%{deKy\x90\xda\xee\f\x81c\xa0\x8e\xcf\x12Ф\"\x82qǀ\xa1\xff\x80\xe1ȗ\xc6m\xcb\x1c\xad\xe2\xe6}ѼꐓkB\x865\x1f\x13\xd6\xf0\xd8\xed\x8bg\xa9\x82\xfdMj=\x8e\xaf\xf0ȉ$\x16\xaa9N\xa8\xe1\xc8,\x16{\xf2~KN\x95\xc611\xf7\x8bUd<\x7f\x1dF\x16~\x96k.\x8e\xc1\u038b\xd7W\xbcbU\xc5\xeb\xd4RdVP<_)d^\xf4yR)\xc0r\xc02]\x05\xb1X\xfb\xf0\xa4\x80\xe6\xa4%-\xd64\x1cSɰH\x9d<1{\xb5Z\x85W\xabPxݺ\x84Y.\x9a\xfdxL\xe5A\x8c\x93~\xa6M\xc3\xc4\xf6\x90)rYg\x96m\x96Y\xe6f4\x91\x01\xcf\xf4Ù.:\x9c\b}\xddq\xe9D$\x19ҖL\x18yN.\xc5\xde\xc3M\xc0酏B\x9a\x83\x83lvZ;\xc6y\xff\xb4\x16\x82\x9d\a\xe5\xcfLjZ\xbbYMy\xfbI\xbaJ5p\xcaO\n\x1c\xbf\x8c`\xf4\xb3\xa3\xaf\xe9\xf9\xd7-7\xac\xe1`=\xbaGV&ϐ\x99\n\xf6\x11\xc9\x7f\x93xBj\xbdGH_\xee\xa2,\x9e\x8f\x82\x18\xaa\xc9\x0e8'4\xc5\x1d\a\xcb/\xdc\xc9\xe4B\xae\xf0H\xa0%o`\x12\x7f\x9e\xf9\xccI1\x1e\x03C\xea\xd5\t\xb8\x05\x15x\xbaY'\x162i\x0es\xb4\xe8\x81_\xee\xa2\v|\xf7K\vjO\xe4#\x960x\xef\xad;\xab\xe0Ս\xb61fP\x80^\x19Om*\x1c\x842\x9d\x82\"\x97\xc2\xf9\x12\xe3\xf9`\x1f\xab\xf9\xbaPͪs\x1b\x85%ǘ\xe8.d\xec\x9d\xe8\xb6\xe4\xf6\xe7\x16\xf5\xbfl\xe0v|\xe8\xb6\xe8+\xe5\xfb\xb3\xbfQ\xb1\xfe)E\xfay\xdbA\x8bE\xf9/\x15\xc8-\x85r\xd9\xdek^\xd1\xfdq\x9b\xa8/Xd\xff\x12\xc5\xf5\x99\x98\xca)\xa6?\x0eO\xafP<\xff\xaaE\xf3\xafU,\x9f]$\x9f\xb5\x8f\x99\xbdi\x95\xbb\xcdxb\xd5\xf7\xf2\xae\xfb|\xd1{F\xb1{\xc6N\xda\xf2\"OX^F1\xfbqE\xec\x194\xcb\x15\xc5W,V\x7f\xc5\"\xf5\xd7.N_ଅ\xcf\xc7\x15\xa1\x9f\xbc\x03\x13\xb6\xfaod\t\xb7R\x99\xa5\xe0\xe4v\xdc>\xb1\x93\xda\v\xd8$/\x89\bM\x13\xab\xc4\x10Ç\x17\xa7-*\xbd\xe9\x19\xdc\xe9\x9fei綴\xc7r7j~pVy\x03\n\x84\xbb\xe6\xe3?\xef\xbf\xdcD\xf8)\x9f\xd7{ƣ\xeb%\x9c\aSz\xe4\xf8\xad9_\xcc䰅>\xc03\xef\x8bІ\xfd\a\xde\xf7\xf6\x84t\xd0\xe5\xed5\xc2\b~\x1a^ \x17\xab(\xe2\x8e\xe5\x1a\xacŊ\xa8\x9a\x14\x8b\xeb\xcd\x00\xe2\xb0\xe2\xb7\x7f\x8d\x12\x94\xeeʬ`1Y\xa8\xf1\xb2\x82w{\xed\xe615\xca'\xeb4\x8a=\x91\x8e#+\xa6\xcaUC\x95\xd9#\xdb\xe8\xb3\xc1\x1c\x82\x99\x99K\xe7L*\xd6\xc3k\xc0\x92\xe8\r\xb7\x7f\xe1^\xe4\xbe\x19\xee\xf6\x8eqw\xca<\xa6ϟ,\x9ee\x91\x0e\xc0`\x9d\xac\xa8z\x1e\xe4\x0e\x82\x8f\x19\x96\x8dҊݒ\x1a\x1c\xb8?i\xc5\xf8Y/{\xfb:e:\x99Wl\x9d|\xb9\x96Cτ\xfa\xc1\x1d\t\xab\xda\x0e1uB\x81\xceb\xb8\x9dq\xf0c>\xb1\x90y5S\x9e\xc18\xe1:&\xc4W.\xaeH\xf2\x96\xa6̛\x98~SD\xcfh5]TP\xb6\x1cN\xbd\x87\xf5\xbe\xd7\x7f\xf9&\xd60Z\xc6]\xac\x16\xd9=\x03m=\xacᝯ\x9e\x12\x1er\x9f\x92SA8&lܕ\x8f\x85\xbb\x1d\xb8(@\xebM\xcbC\xe5h\xa1\x80\x1a(Cs\xa6㌏\xaa}\xd4\x0f\xac\xf9\xe0j(\xc7a\xf7I8\x9e\x06\x17.O\xefY\a\xdcSP\x8f\xa0V\x05z\x84\xad\x822Tt\xcex\x91\xa4\x0e \x99\xeeG\xf2\x03W=\xd1\xff{\x05\x02\xb9\xd29Q\xa1t\xb4\x0f͢\xa3\x81\x92\xc0#\b\xc26\xa47/)z\x13N\x81\xffLq\x03\x0e6\x1b(\x8c\xdbХ\xe80\a\xa9=\xc0\b\xeb\xe4>\xe1Y=\xc1\xe0\xb5\r\x97\xb4\x04\xe5\x1c\xed\x05B\xfeנ\xf1H\x13\x05\x04t\x97(\xcf^@\xfb${\xd4PE9\a\xfe\x89q\xd0\x1f\xe4N\xd8ye\xa8\xd9\xdbT\xbf\xde\t\xe8\xa2U\xd6Y\xdb\x13\xd1\xd6kPD\x831\xd3iٍT\xf3g\x91\x1c\xe2\x990\xb0\x85T&{\xa7\x98\x81\xfb\x86*\r8\xa3\x8c\x15|\x1fuqy\xde\r\xa7[Wt^\xb2\x82\x1a\x88\x82\x83#LM\x1f\xfbk\x84\xc5\xf7X\x03,'\xb6\x97\xb2U\xf5\xd4\xe1\xc7Ie=u\x91w\xc2\x01K^\xe5\xed\xfc\xac\x826\x06\x8f\x9a\"\x1d\x91\x88\xc6\xc3\xc0\xeb\xf1G\xb7y\x0f\xc0Ns\x9a?0\xe4Kӵ\xa1u\"\xf6[\xd6tW\x87`\xf0\x02~U\xf6*\xdc\xfbW\x19\xc7Rv\xb2\xa3:\x1e[JFT\x1dl\a\x06՚\x05\x1d4\x93\x15E\xca8\x94s\x9c\xfa5j\xab\x9ft\x84\x835\xf7\x96\xc5\xef\rU&N\xfd\xd0;u\x91\xf9\x05)\xa9\x81\x95\xed}\x9a~J_H\xaeԉ\x857x\x86܋G\x11\x0e\xb8Z\x9fƝ\xfc\xaeAk\xba\r\xe9\xde\x1d( [\x10\x16\xefq\x17/\xe9\a\x87\xc3\xf3\xde\x05\x18\xa4{haZ\xea\ap\x8ey\xacS\n\xbf\x04\x80\xf9\xe2\xed\xa4\xe1M\xab\n\x7fL\xff\x0e\xa8\x1e\xff\xb0\xc4\x01.>\xf5\xdb\xfa\xedX\xb7bW\x85@\xddQ\n\xfci\x01\xc3b\x0e;%\xd3F\xe2\xc8G9\t\x95\x94\x0fY\xc1\xd3\xe7ذ۸a±\x12^N\xb0\x96\xad\xe9y\xaf\x1e\xe1\x89i\xe2E\xdb\xcfl_\x10\xe6\xa5;\xaa<\xb5\x8b\x99\xe7\xbf\x7f\x1e@\x8aI\vi(\x0fF\xc6\xf2elP\xcd\\\xd5s\x1f~\xa6\x80\xf3\xfd\xd9\x18\xf2\xe8\xf7O:\xd8Uwi\xb6\xd7\x04\xddE-\x13\x03\x85\xfd\xb5$\x90x\xdfv\xe7iN\xddn\xbcd\xff\x10\xea'\x9cT\x06\x8e?w\xad\xa7\xf0\xe8\xa6\xe9\xc2 \x10\xe9\xfc\x01\xc1\x90\xd2TQ2N\x98\xfaL\xec\xd1TT/\x05\x1d\xb7\xb6Mt;z\xe6*\x86\x16w\x13R\x99\xbeQbEn`\x97x됅u&(U\x89&\xd7\xe2Vɭ\x02}\xc8t+\xbc9\x80\x89\xed'\xa9ny\xbbe\xe2\xcb\xf4\x19\xab\xb9ƷT\x19f\x99\xd6\xcd'\xd1\xf7*ظķ\xe5\xde\xd3\x1f\x98\xa0\x9c\xfd\x9a\xd2\xe5\xfd\x8fK#\xcc\xe8\xbb\xc6#\xef\x14\v\x15\x10\xbf\xa4\x00\xbd\x86\xfeI\xf7\xccO\x18\xf7\x9c\xdcȤ\x18\xfbR,6\x04\xca4Y\x836+\xd8l\xa42n\xa7|\xb5\xb2\xe1\x8bw\x90\xac\x86\xc0\xe8\xdf\xfdn\fa\xa9\xe8*\x16\xb9\x04\x87e\xe3\x13\xc4\n\xad\x0e&\x12j\xbawyfZ\x146&\x80w\xda\xd0T\xc4\xf9$=\x8d\t\b/+9*\xe4\xba\xdf>fn\xa3\xfa@p\x0euxq\x8c3\xe8|\xaazdp/\x15\xd1\x16{\xa7(\x13\xe2\xd4\xd8\xf5t2%\xcf\xd4|\x8dP\xa6ԣ_\xdf\xe0'/|)\x93od\xc9VTTl'\x8f\x8eWJ\xb6\xdb*\xf0\xe6\x94CD\xca\x16\xf3!\r\xaa\x02\x1d~\xa2˴J\xf4\xcac|5㔖\x8eӝ\xf6Q\x9e\xa0\xa8Uw\x84\xb4SU36?;\xf7;\x01q\xd1\xf6' R\xbd\x17\xc5\xeca\xd7Ýǣ\\\xcb$\x12\xa26~6$D\x88SH\xe8\xfb\x12]\xc4\xf3\xbb\xc1Ȕ\x8fr\":\xe6\x9d\x18\\\xe2<\xa8\xe5E\xf7\x9d\xa0\xa1\xbbs\x1c:\xf4 \xf8;)\xd17\x80pL\xe4\x8bc\xa7\xe3\xde\xdfo\xc4\xfa\x18\xbd\xad\x8f'Ǯ\xdfF0F\x97\r\xd8(\xb6\x1b&ě\x7f\xcf6)yq\xbf\x83\xb8\xe6\xf0\x0f\a__\xf9Ҁ\x1dU\x82\x89\xedI\x18\xf9\xee\xfb&\xe2y\x0f\xf6%#\xfa0\xf3g\x8b\xe9\x93f\xe9\xe0%2x\xd9ó\x1fɿ\xf9\xbf\x00\x00\x00\xff\xff\x9d=\x85\t\xc7t\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec=\xdbr\x1c+\x92\xef\xfa\nB\xfb0\x97P\xb7DZ\x97\xd8ЛG\xb6g;\xc6c+,\x1d=\x0f]\x95\xad\xe6\xa8\n\xea\x00%\xb9wv\xfe}#\x13\xa8[\x17]T\xab%ϙ1/\xb6\xaa!I2\x93\xbc@\x02\x8b\xc5\xe2\x8cW\xe2\x0e\xb4\x11J^2^\t\xf8fA\xe2_f\xf9\xf0\xdff)ԛǷg\x0fB\xe6\x97\xec\xaa6V\x95_\xc1\xa8Zg\xf0\x1e6B\n+\x94<+\xc1\xf2\x9c[~y\xc6\x18\x97RY\x8e\x9f\r\xfe\xc9X\xa6\xa4ժ(@/\xeeA.\x1f\xea5\xackQ\xe4\xa0\tx\xe8\xfa\xf1\x0f˷\xff\xb5\xfc\xcf3\xc6$/ᒙl\vy]\x80Y>B\x01Z-\x85:3\x15d\b\xf4^\xab\xba\xbad\xed\x0f\xae\x91\xef\xd0!{\xe3\xdbӧB\x18\xfb\xe7\xde\xe7O\xc2X\xfa\xa9*j͋N\x7f\xf4\xd5l\x95\xb6\x9f[\x98\v\xf7\xbb\xfbM\xc8\xfb\xba\xe0\xbamsƘ\xc9T\x05\x97\x8c\x9aT<\xa3\xaa~l\x04b\xc1x\x9e\x13\xb5xq\xad\x85\xb4\xa0\xafTQ\x97\xb2\xe9 \a\x93iQY\xa2ƍ\xe5\xb66Lm\x98\xddB\xb7\x1f,?\x1b%\xaf\xb9\xdd^\xb2\xa5\xa1z\xcbj\xcbM\xf8Ց\xcf\x01\xf0\x9f\xec\x0eq3V\vy?\xd6\xdb;v\xa5\x95d\xf0\xad\xd2`\x10e\x96\x13s\xe5={ڂdV1]KB\xe5\x8f<{\xa8\xab\x11D*Ȗ\x03<=&\xfd\x8fS\xb8\xdcn\x81\x15\xdcXfE\t\x8c\xfb\x0e\xd9\x137\x84\xc3Fif\xb7\xc2L\xd3\x04\x81\xf4\xb0u\xe8|\x1a~v\b\xe5܂G\xa7\x03*\b\xf62\xd3@2}+J0\x96\x97}\x98\xef\xee!\x01\x18\x91\xa8\xe2\xb5\xf1r\x14Z_w?9\x00k\xa5\n\xe0\xf2\xac\xad\xf4\xf8\xd6\xc9^\xb6\x85\x92_\xfaʪ\x02\xf9\xeezu\xf7\xef7\xbdϬO\xd1\xff[4\xdfY\xc3\r&\f\xe3\xec\x8ef\x10\xd3~J3\xbb\xe5\x96i@1\x00i\xb1F\xa5a\x11H\x9d3\xa5;\xa0*\xd0B\xe5\"\v,\xa2\xc6f\xab\xea\"gk@n-\x9bڕV\x15h+\xc2|r\xa5\xa3z:_\x0f\xa1\x8f\x05G\xecZ91\x05C\x92\xe9g\x1b\xe4\x9eHn\xf2\bӎ\x878\x88\x9f\xb9dj\xfd3dv9\x00}\x03\x1a\xc1\x84QdJ>\x82F\x8ad\xea^\x8a\xffm`\x1b\x9c\x12\x96$Ղ\xb1\x8c\xe6\xb3\xe4\x05{\xe4E\r\x17\x8c\xcb|\x00\xb9\xe4;\xa6\x01\xfbd\xb5\xec\xc0\xa3\x06f\x88\xc7_\x94\x06&\xe4F]\xb2\xad\xb5\x95\xb9|\xf3\xe6^ؠ\x903U\x96\xb5\x14v\xf7\x86t\xabX\xd7Vi\xf3&\x87G(\xde\x18q\xbf\xe0:\xdb\n\v\x99\xad5\xbc\xe1\x95X\xd0@$)\xe5e\x99\xff[\xe0\xb7\xe9u\xbb73]!u:\x83=\xa8g\x9dt9Pn\x88-\x17\xf0\x13\x92\xee뇛ۮ\xe4\t\xe3\x99\xd2\x11\xc0\x18\x7f\x90\x9aBn\xc0낍V%\xc1\x04\x99WJHK\x7fd\x85\x00i\x99\xa9ץ\xb0(\x06\xbf\xd4`,\xb2n\b\xf6\x8a\x8c\x16\nm]\xe1\xdc͇\x15V\x92]\xf1\x12\x8a+n\xe0\x95y\x85\\1\vdB\x12\xb7\xba\xa6xXّ\xb7\xf3C\xb0\xa7\x11\xd6\x06]qSA֛j\xd8NlD\xe6&\x14\xaa\xe4F\x95\fԲ+㳟~!\xdd7\xfc:\xc0\xc3)\xc8\xd0+\x184JvK\xccom#\x8a\x9c\x83ƔfR\xd9=\x98\xfb\xaa\xb5C\t\x0fe\x02\x93=ag{*5Œ\x8e\x00im\xebP\xbe\xa2\xac&\xbc\x1fD\xb5*K\xc8\x05\xb7P\xec\x8eB\xbf\x0fb\x8c̊\xfaak\xa7\xe7ŦG\xf4\xbc\x06&:\xedi2\xfe5\xd4ط\xc6\x7f%\xcbNF\xd4Ѥ\v\xac\x96-\x0f\a\xfdHx\xda'\rc\xab\r\xb3\x1au\xae\xc7\xeeI\x14\x05\xcedĸ\x82\xbc\x87Z\xbc;\xb1a\u0086Ѭ9a \xd9\xd2yQ\xcb\xd6gh\xec?\"8\xc0\x8eԾ\xeb\x1f=\x15n\x99\x84o\xb6\xad\x85Î\x8c`\xc3\v3\x18\x82WH\xb3\x86q\xc1ֵ=\x0e\x03(+\xbb\xbbpm7\xaa(\xd4\x133\xa4l\xd1\bn\xc4}\xad\xddd\xffm\x0e\x1b^\x17\xf6\xd2\xe1\xfc\xbb\x98\xb4\x8eO3\ve\x85&\xf3\x189\xbd\xf5mq\xc08[\xf2&\xfe\bnr\xf0C\x94w?F\x80(\xe7\xc5VZ=\x8aܛ\xf3=u\xc5\x0e\xaa,,\x8eQ\xb78ґ_\a\xa3\xf9cS\xb93\xb7\xb6\xea\x89=b\x00\x00\xe8-r\x1c\x14\x02\x85\x9c\xd5\xd5\xc5(Lƞ\x84ݲJ\x19#\xd6\x05xρ\t\x99\x15u\x8e\n\xe6c]\x14$\x82+\x99i(\xd1\xe1)Ƹ\xcd\x18Ⱥ\x1cG|AP\"?u\xe0\x8e\xd68\xa0\xa7\xb0dF\xdcH^\x99\xad\xb28\x8dTm\x13\x887*\nX\xaenV\x03h\x1d\xea\"\x8f)~\xa0\xc9m\x15{\xe2\u0092\xa1\xba\xbaY\xb1;\xa2{h͜\x86b\xb6\xd6\x12\x9d\x83H\x7f_\x81\xe7\xbb[\xf5\x93\x01\x96\xd7\xe4ׄ\x98\xe0\x82\xada\x83N\x80\x06\x84\x81?\x81\xd6h\x14\r!\xa1\xea=?3\x94['\xc78\xa1\xbc\x9b$\f{\xfb\aV\nY\xdbѩ:Ie\x94\xa6R=\x82~\x0eq\xdfs\xcb\xff\x82@\x064%Q%\xe8~\x96\x11}\xd7;\xfaq\x1d1_\xae\xac6\x1d\xa8°\xf3sT\xa1\xe7.\x86?\xbfp\x10jQ\u0605\x90\xdd~\x82>Ǟ\x8e#\x88\xa3\xafc\xba\xb9U\x1f\x8d\x9b\x90ϢO\x04\xe6\x88\xf1\xacT\x1e&\xfaF\x14\xc0\xcc\xceX(\x83\xaaoåN\f8,\xe4`\x15\x85\ac\x90\xde~P\xe3\x04\x91uQ\xf0u\x01\x97d\x19\x0f\xd0l\\I\x8f\x11\xed+\x18+\xb2S\x92\xccA\x1c!\x98\xf6?\xf4(C\xf1\x16\x7f\x00\xc6#\xe0==1\xb8+\x8a\x0e\xd1\xfbԊ\xe2Vi\xc8\xd0\xf1\xbf\xf4\x01\x85\x80\x82\x82\x18\xa9X\xa1\xe4=h\x87Ec\xe0\xd1\xc0\x00\nh\xce\xd0W\xd7h\x96\x85d\x9b\x1a\xdd\xf8%C-\x11\x95\x11!\x8d\x05\x1e\x11\xe6\x13\xf0\x0e\xbe\xa1E\x80\xfc\xaa\xa8\x8d\x05}\x93\xa9\n\xf2\xb0j7j\xceRy\xf8\xe1 d\x1f\xf4\x15\"\x03\xe4C\xe6*-he,&\xdam\xfc\x87\xe6\x91\x16\xf1\x90\xd5~\bm`7\xa9[\fXlx\xfe\xfb\xf3\v\x92\x80~\xef\xfd~\f\xe3\x1a\x1a2\xcd\xd2\xcd\xe4&\x8d\xb7\x10\x16\xca\bu'u\xd4\f\xbes\xad\xf9\xee\x00כ\x15\xc8\x17\xe0{\f\xf6\x80\xf32T\xfbN\xbc\x1f\xf6\xff\xaf\xc8\xfd\xd3\xf2\xdb\xd0*>\x17\x12\xf9\\\bc{l6n\xe9\x0f\xc9:\x16w{\x02I\a\x13\xd5\xe4\x14W\xffA\x88yҹ\x13\x9b,\x8dl\xfa\t\xf0OEɭR\x0f)\xd4\xfb\x1f\xac\u05ee\xfb\xb1\x8cv\x9a\xd8\x1a\xb6\xfcQ(m\x86k\xcb\xf0\r\xb2\xdaF5\v\xb7,\x17\x9b\rh\x84E{#\xcdV\xca!b\x1d\x8e\xf9XGeE+\f\xc6\xd52\x1dYJԈ\r\x85\xa2\xfa(T\xe7\xe0`hA\x0eD.\x1eE^\xf3\x82|\t.37>\xde\xe0\x17\xd3j\x13\x02\xb1\x87\x7fT\xaa]q\x0eM\x18$2\xb1\xb7T\xa8$\xa0\x8f_bl\xb4_5N\x89\xb0\xfer\xb0od\xa6\xae\v0\xbe\xbb\x9c\xdc\xe4V']\xb4\xccr\v3\x05_C\xc1\f\x14\x90Y\xa5\xe3\x14J\x91\x03WR\x95n\x84\xb8#Z\xb6\x1fm\xb5\x83\x99\x00\xcb(\xc4݊l\xeb\xdcW\x144\x82\xc5r\x05\x86\x96\x92xU\x15\x11\xd3ՖI\xe1\xf0\x9dM鍶$h\x90!ܘ.iK\xa2~n\xcb(\xd9۹٧\xfa\xf8\xe6\xc8(\xbe\xffJD\x0fV\xe7Ha\x9f\xd0$\x8c6Y\x92\xe7C\x94\xf4Hq\x01f\xd9Y\xd2\x146|Mah\xcf\x7f\xdc\xdb\x7f\xda#ʯ\x8bw\xc7M\x98\x19\xac\x9b\x9cS/˸\xa6\x9b\x7f\x12\xbe\x91ɺ\xf1\x16k\x16\xcf>u[^\xd0V\x8agH~\xc16\xa2\xb0@N\xd5\x14\xa2l\x06\xe7NI\xa0T\v\xcchg\xddf\xdb\x0f͆[B\x8b\x01\xad\x86\x00\x9c\x83\x1e\xa2\x1c\xe2A\x02Hָ\x16\xb4\xd3,\xdcJ\xb9YR$\xd9\xfdB\xae\xe0\xbb\xcf\xef\xe3\xb1g\xb7$J\xeaޠ\x12&\xad+\xef\x06\x8eQ\x17W\x1f\xaa\x84_\xc8_k\x02A\xb7\xffp\xc18{\x80\x9ds\xb1\xb8d\xc87\x1e*'\xa2\xa0\x81\xd2(HS<\xc0\x8e@\x8d\xe7E\x8c\x979\xd2\xe2\xca\x03\x8cl\x95\xc6J\x8f\xae\x88\x9f߀rt\xc3\x0fD\x98\x94\xd9Ԗ\x86\xa8~\xfa\x8cd%\xc4\xcb\f\xbd\x14J\xe0ˑ\xc3N\x16\xa7n_\xfdL\xa2\a\xd8\xfd\xc68^\xe3,\xdb\nک\xe3\xb4z\xa36\xb3\x18\xee\xca\x1d/D\xdet\xe6\xe6\xd5J^\xb0\xcf\xca\xe2?\x1f\xbe\t\x83\x1d˜\xbdW`>+K_^\x94\xcan\x10\xafA\xe3\xb0\xfb\x87\x03t\x96\x04\x89\xd8\u0378q\xb6\x14\x05\xb5\xe1\x870l%1$s$\x9a\xd1\x1d%X\xb9.]gemh\x7fZ*\xb9p\xcbbc\xbdy\x1e(\xddc\xc1I:\xf6\x9dޢ1r\xbf\xb8T\xaf\x82g\x90\x87-:\xcaA\xe2\x16\xeeE6\xa3\xcf\x12\xf4=\xb0\n\xcdB\xba\xb4\xccP\xd4~d\xf3\xc5+\xdds\xe8\x96o\x8b\x87z\rZ\x82\x05\xb3@\xb3\xb6\xf0P\xac*\x13\xe9\xe2m\xc2H\xa2\xceXY\xe0\\O\xac\x19\xa4%\xa9z$\x8d\xe9p\xf5Tb=\x93L\xe4E\x90ە$\x05\xddl\xe0y\xd6k\xa6\xdc\x1c\xa3b:cq.@\xc9ik\xedoh\xe9i6\xfe\x9dU\\h\xb3d\xef(\x1d\xba\x80\xdeo~a\xb2\x03&\xb1ۊV\xd9\x7f\xa9\xc5#/\xd0\xff@\x03!\x19\x14\xce\x1bQ\x9b=_\xed\x82=m\x95qnC\xb3iw\xfe\x00;\xb7\xa3\x9c\xd4mWa\x9d\xaf\xe4\xb9\xf3e\xf6\x14O\xe3\xf8(Y\xec\xd89\xfdv\xfe\\\xf7n\x86DϨ\xda\x13\xe5\x92W\xe9\x92L\xc9\xc6s\x02\r\fփC\x84\x8d\x9b\xac[\f\x10\xa6(\x90,ʕ2\x91d\x91\bZ\t\x82~\xad\x8cu\xeb\x90=\x7f\x7ft\xa1R\x85\xc5I\xc67\x1643V\xe9\x90NJ\x8a?e)\xbe[n\xb7`\xc0\xefC\xf9EO\a\x18\xa3\xd8\xf3V78\xabr\xee\xf6¨#\x9e\x91\xf7Dm+\xad20Ѽ\x88\xb6$ڦ\x91\xac\xa5.\x1d\x9au]\ue8bfM\x92\xd6NY\x94\x0ee\x9e#\x8f\xa4;\"2\xfa\xf0\xad\xb3D\x8d\xda\x05\xffN\x91\xd6cpdt\x00\xa6,\xf90\x87:\x19\xdd+\xd7:\xcc1\x0f̅[\xfa\xbe&\x9d3\xc7\xebhD\xf9\x1f͵)\x85\\QG\xec\xed\v\xbaC^\x8b\xc7ң\xc6\xca\xf1N\xfaU\xe8\xac\xe5^\xf3\xc1'\"*\xda\xf8\xd1\xd0c\xee\xfe\x9e\by\xd7R\xd9\xce2\xceL'\xbaR\xf9o\f\xdb\bml\x17\rs \xb1j\x14\xd4\x11\xa1\xa7\xfc\xa0\xf5ё\xe7\x17\xd7z\x90:\xe9\xb2\xcd\xe7\xc4ہ\xa4[\xfe\b>\xdd\x17d\xa6jIKa\xa8\a\xb0\x9b\x19\x10\x1dk\x9c\x15H\xb4w\x9d\xc6\xd1\f̱\xb2 I\x12rrݬ\xdb\xe4#\x17i\xebV\xec8\xb6\xdaC9\x9cc\xe5\xf8y\x14\x12<\xbbg\x10J\xfeM\x94u\xc9x\x89<$\xb7C\x94\xd0\x1cCp\xecn\xd2>\xb1\x05\x19-\xabp\x96U\x05X\xf0i\x9b3\xf0Ȕ4\"\x87\xc6\xf4{\x11P\x92q\xb6ᢨ\xf5\f\xad:\x9b\xe4s\x830\xafMN\x1fY\xa5#\xb2 \x12%\xae\xb3\xcf\xf0\x82\xa75~\xa5\xe7\xf9\xb1)\x0e\xa3\x86\xf9\xfeb\xa5\x85r'(N\xef2\xfa\xb4c.w?|\xc6\x1f>\xe3\x0f\x9fqNG?|Ɖ\xf2\xc3g\xfc\xe13\x1e.?|Ɣ\xf2\xc3g\x9c\x89\xc8\xf7\xf2\x19S0\\\xd0\x1a\xe7\x81\nIX%\xa6BL\xa1=їO\xfa\xf1g5N\x92˼\x1a\a9r\x88'r\xfc\"\xe6u\xb4ƫIn\xc6\x19\x18\xe6\x8e;z\x9a\xe00\x9f\xe0\xf4L@\xe0\xf4\xa7gV\a!\x9f\xf0\xf4\x8c\x1fBZ\x84q\xd4ٙ@\xa4\xf9\xa7'.|\x12Q\t\xaaA\xfe\x83mU\x1d9\xb51Aڄ,\xda4\x82\xf4\x92j}b\x04X\xfe\xf8v\xd9\xff\xc5*\x9fbK76D\x80\xd1%\x1e<\xa7\x9b\x1b:\az\xbc\x1e\b\xf7K\r\x852\x02Li&E\xe1$6@\xe8\xc9+\xfbR\xb9\xd5\xc1\xa3\xfd\xa6\xe95\xac\xf4Dܹ\xe9\xb7M\xb6\xe4\xb4\xfb\xfe\x8c\xa4ۓ\x1e\x8d\xfani\xb5\xc7%Ӧ\xaeP&$Φ\xa7˦\xb0Օ\xf4$\xd9\xe4\b95!v\xee\nċ&\xbf\xbeL\xcak2\xcd\xd2\xd2[\xe7R\xecURY_9\x81\xf5\xf5\xd2Vg$\xab\x9e\xfe\xd4K\xfaZ\xfa\xd1ٕi\xcb2\x87\x13N\x93\xd2L\x93\x96nR\x06|\xd4P\x93\xd2G\xe7&\x8d&q2}\xba\xbejZ\xe8\xab&\x83\xbe~\n褴MV\x98\x9b\xe49~3d(\xd3\x0e\xc0\xde\xc5Σ\x95^BBg\xad\x97\x1e\x9c\xc0\xc5\xf7\x98`\xcfe\xb5ҽ\xf0\xe2Y\xb1\xf3\x97\x01,\x14\xf8\xe0j\xbfb,Sօ\x15U\xd1^\xc4\x17\v\x9a\xb7\xb0k.\\\xfaY\xd11\x7f\x7f\xdbؗ\xafͬ]\x0e\"3n\xd8\x13\x14\x05\xe31\xfd\xb2G\x85\xcc]\x00\x9b\xa9\x05\xa0}GM\xe5/\x94\xf2\xb7\xc6^\xb8)O7\x1a\x90\x97PƖ+\xb9<|[\xd9A#\x9c\xaa\x8b\xf7\xa2\v\x173ѷ_j\xd0;Fw\xa75\xfee{0\xd6++\x83\xc1uP\xa1^\xa5\x1f\xda\xf7\xd9\v\xd2Z\x15\xc7\xdeI\xe7\xd5\fq\xa26\xa8;۠\x14\r\x83\x8c]0\xc8\xc2\x04\xdd\a!U\x03!\xd24%\x80\x99sR\xf4%B\xd4S\x04\xa9I^\xdc<\x0f\xfc;\x9e\x00=\xf6\xe4gzBM\xd2Iϗ\bY\xe7\x04\xad\xb3|\xee\xf4\x93\x9c\xf37\xcf_\xf8\xe4\xe6K\x9d\u061cA\xbd\xd4\x13\x9a\xf3i\xf7J'2_\xfd$\xe6k\x9e\xc0\x9cu\xf229\xc5lV\xd6Ĝ\xf4\xb0g\x1c\x19Lˇ\x98>I\x99x\x8221[\"m\xf0G\x0e;\xf1\x84\xe4\xfc\x93\x91\x89\xfc\x9d3\xa5_\xf9\x04\xe4+\x9f|\xfc\x1e'\x1e\x13$0\xa1\xca\xfc\x93\x8d\xcf\xdeVS:\a=\xb9u9Gj'\xe555\x96\xeb#6؛\v7\xe2b\xad^\f@fɿ\xe0@\xafu\x1c\xda\xcaG\xc9\xecxD\xbd\xbd\xd5\xd6]\xeb;\xc4\xfe\x19\x0f\xb7\xfdj\xa0\xe2h\x00(p\xa3\xf4\xb2\xa8\xab\xf0\x81g\xdbA\x0f[n\xd8F\xe9\x92[v\xdelx\xbfq\x1d\xe0\xdf\xe7K\xc6>\xaa&ߨ{\xe7\x9b\x11eU\xec0\x12c\xe7\xdd\x06ϓ\x92\xa8t\x86\x9e\xafU!\xb2\x88\xcf9z7\xa0k\xb0wa\x12\xdd^\x98u2^b\x81\x0f6\x17\xe1\xe6\xc8\xfe\xb5\xd2\xee\"\xffc\xd7{*\xf1'zg\xeb\x04+\x87\xef\xaeW\x04+\x88\x11=\xe0\xd5$Y6,_\x03\xba\f\xed\xd8\x0f\xe9\x93զ\a\xb5\x9f\xe7\xdc}\xa5\x04r\xf7$Mp[\xbcj\xce\x14j\xad\xeb\x95\xc3\xe5PO(_\\\xee\x98\xf2o\x8e\b\x9d/*\xae\xed\xce%D]\xf4\xf0\bv}j\xe5\uf835\xda\x7fr\xa7[zd\x0f\xaf\xed\xd0n\xfc\xae\xea'@\f\xe9\xf9\x1c\x9c\x0e\x9f\f\x9f<\x13\xfe\x028\x1dv\xa1\x16D\xc5\xc8O\xd1,Γ\xaf\xba\x1a\xff\xca\xc0_\xd4#\xbc\x8f\xae\xbe\xf6\x9f\xdd\x194\x19I\xaf\fP\xe9\xa2\xfc\b\x05ۜJ\xba\xa7\xfcyj/\x9e/\x19P\xf1\xf7\x9c?gq\xf2\xa6\x0fj\xfc%\x1a\xba\x05>t\x1a\xf3\xaa药\x1d\xbb\xbe\xa3\xb8\xb5Q\xa5~\xea\xfb\xb85,O\x86$\x89\b,!\x0f>\xces*2Z\xa5\xf9=|R\xeeQ\xa5\x141\xe9\xb7\xe8=\xb9\xe5=\xb7\x90s\xee'aL\xd1\xfb\xb1\r\x01\xb6gL\xf6\x1e+@l\x8f|\x8e\xc1\xda\xe292r{\xfbɍ\x94\u07b2y\uf7e5A}l\x00Y\x10(࠭\xf1\xbf[\xf5D\x97\xf8\xc7ט\xc3\xcb1\x9d\xc7\xeb\x80\x0e\xbbP\x1a\xf2Qì\xabB\xf1\x1c\xf4\x15\xbd\x9e\x930\xe2\x9fz\r\x06\xee@\xff\r\x1eo7#\xe3\t=\xbf`\xa6\x0fztE\x01\xc5GQ\x80q\x88'\x9a\x86\xeb\xfd\x96\x8d\xa5\xa8˵\xf3T7\xf8c\xd3\xc9\x01\xcb\xec\x86J\x1b\f\x15h\xf4\x13\xddVDm\x82\xe4\x1f&\x06k\xf8(\xa4\x85{\x18\x8f\xa1'l\x82{g\x82\x1c\x80\xa0\xc0(\xe2\xfbsl\xe5\xb1G\x90\xbbx\xeb\x81\f4\x8b\x9119Vޭ\xba\xbe\xbb2\xac\x969m\x00\xdc\xfd\xe9\xe6(\xf9}콑\x13tB\x8az\xbf\x1bo\xd9\t\x11:ډ|\xfa\xb8\x12\x8f\xc1\xe2ƨLPTA\x8f\"\xd1i\xaa\x97\xbb\a\xfdP\x80x@:j\x03_\x9e$\xe8\xaf\xc1\x02\x99\x95\x8c\xbd=3\xad\xfd~ڃ\x16}s\xc6*\xec{\x04\xc6\x00\x00Sa\x9f˸\u05cc\xc2\xf6\x9a0ͫv\xfb\xf4\x9cP!qK7\xee\xb0-\xc6\x1f\xe1Z4\x8f\x85\x9d%\x90۽\xe1\xd4\a<\xfe\x96\xa1{\xec)㕭uЮ\xb5\xa6\x9b\xe2\x11\b\xb8\x8bԏ{Ͱ}\xe4\xee\x18\x06\xb7\xaf̵\xfb\x0f\x93\xef\xe0\x8e\xc0i\xde#\x8c>n\xe6\"j\xf7N\xed\x02\xe1\x1f\xc7\xe3\xd1\x19\x838߸G\xeb&\x88\xf0\xa9\xad96\xe0f\x188d\xff\fޫ\x8e\x84\x1e\x0e\x98\x18\xc35\xd6iN\xeaz9\xa2\x86\xe1\xc1\x81\x9b\x18\x13Əs.\xd8g؏\xd8\x17\xec\x83\xc4A\xec\x13\xc0\x9dل\x9c\xb6VH;\xce\x19\xe2cӊ\x0e̎h\xc8i\xb1\xbd\x1b\xc0\x18d\xe3\xd3\xc3UM\x15wbְߊ1o\x94v\xcc2\x1c\xe8\xef\xf6~\x8dj\xf0\x83\xda;\xa6\xb9G\xd5\xc8\xdeGz\x011\xefH\x8e\xf7һ_\xeau\xfb(\x04\xfb\xdb\xdf\xcf\xfe?\x00\x00\xff\xff\xf8FZ\xc4S}\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcV\xcfo+5\x10\xbe\xe7\xaf\x18\x89+\xbb\xa1B \x94\x1b*\x1c*\xe0\xa9j\x9ezw\xbc\x93d\xa8\xd7^f\xc6)A\xfc\xf1\xc8\xf6n\x9b\xee:\xb4\x8f\x03\xbe\xad\xed\xf9\xe6\x9bo~x\x9b\xa6Y\x99\x81\x1e\x91\x85\x82߀\x19\b\xffT\xf4\xe9Kڧ\x1f\xa4\xa5\xb0>ݬ\x9e\xc8w\x1b\xb8\x8d\xa2\xa1\x7f@\t\x91-\xfe\x84{\xf2\xa4\x14\xfc\xaaG5\x9dQ\xb3Y\x01\x18\uf0da\xb4-\xe9\x13\xc0\x06\xaf\x1c\x9cCn\x0e\xe8ۧ\xb8\xc3]$\xd7!g\xf0\xc9\xf5\xe9\x9b\xf6\xe6\xfb\xf6\xbb\x15\x807=n@\x90ә\x1a\x8d\xc2\xf8GDQiO\xe8\x90CKa%\x03ڄ\x7f\xe0\x10\x87\r\xbc\x1e\x14\xfb\xd1w\xe1\xbd\xcdP\xdb\f\xf5P\xa0\xf2\xa9#\xd1_\xae\xdd\xf8\x95\xc6[\x83\x8bl\\\x9dP\xbe \xc7\xc0\xfa\xe9\xd5i\x03\"\\N\xc8\x1f\xa23\\5^\x01\x88\r\x03n \xdb\x0e\xc6b\xb7\x02\x18\x05\xc9Xͨ\xc5\xe9\xa6\xc0\xd9#\xf6\xa68\x01\b\x03\xfa\x1f\xef\xef\x1e\xbfݾ\xd9\x06\xe8P,ӠYֿ\x9b\x97}\xa8\x85\t$``\xa4\x04\x1a\xc0X\x8b\"`#3z\x85B\x19\xc8\xef\x03\xf79\xad`v!\xea\x05\xaa\x1e\x11\x1e\xb3\xfec\x98\xed\xcb\xe1\xc0a@V\x9a\xa4)\xeb\xa2\xe2.v\xff\x8dxZ)\xd6b\x05]*=\x94\xecy\xd4\v\xbbQ\x1e\b{\xd0#\t0\x0e\x8c\x82\xbe\x14c\xda6\x1e\xc2\xeew\xb4\xdaΠ\x8b.\x922\x19]\x97*\xf6\x84\xac\xc0h\xc3\xc1\xd3_/ؒ\x04JN\x9dѬ\x9dWdo\x1c\x9c\x8c\x8b\xf85\x18\xdf͐{s\x06\xc6\xe4\x13\xa2\xbf\xc0\xcb\x062\xe7\xf1[`\xccRo\xe0\xa8:\xc8f\xbd>\x90N}hC\xdfGOz^疢]\xd4\xc0\xb2\xee\xf0\x84n-th\f\xdb#)Z\x8d\x8ck3P\x93\x03\xf1\xb9\x17۾\xfb\x8a\xc7Ε7n\xf5\x9cjP\x94\xc9\x1f.\x0er\xeb|AzR#\x95b*P%\xc4\xd7,\xa4\xad$\xdd\xc3\xcf\xdb\xcf01)\x99*Iy\xbd\xba\xd0e\xcaOR\x93\xfc\x1e\xb9\xd8\xed9\xf4\x19\x13}7\x04\xf2\x9a?\xac\xa3\\\xb8qד\xcaT\xda)us\xd8\xdb<\xab`\x87\x10\x87\xce(v\xf3\vw\x1enM\x8f\xee\xd6\b\xfeϹJY\x91&%\xe1Cٺ\x9c\xc0\xf3\xcbEދ\x83iv^ImeJl\a\xb4)\xb9I\xdfdM{\xb2\xa5\xad\xf6\x81\xc1\xd4L\xda\x0f1\xc9\x16_\xc8e\x9cH\x85\xcdlN\xa5.\x7f\x9fM},哣\x11\x9co\xce8ݧ;s\xff\x8e\xf6h\xcf\xd6a\x81(S\bߧ\x92\x16\xfa\xd8/}6\xf0\t\x9f+\xbb\xf7\x1c҄\xc6\xf9\xa8\xb9Z\x1bP\x1e\xb1\x03\xf9E\xb8\xf3\xc8ʭ\xfc0.G~\x0eh\x04\x02\x8eާ\x96\x0e~\x01Yy\x11\x16wH\xb1\xaf\xb0\xa9\xf2\xb9\xf3\xfb\x90\xff\"Lrl\xb4\xb4\x13\x8e\xc9\x1e\xfd\x14^\x15\xc0\xeb\xb9.k9\xe7>$hY\xf9y\xfeo\xc6i.\x11c\xd5w\x93YU\x0f\x92ǚ\xe2\xf5\xfe\x1aYF\xe7\xcc\xce\xe1\x06\x94\xe3Һ\xd8\x1afs\x9eW\xcdTj\x9f\xa9GQ\xd3\x0f\xef\x14\xd0\xe2UH\xeb~\x81\x92\x9a\xe7\xf9\x88\xfeZ\x8b\xc0\xb3\x91W\xe7\x15\xc8\xdd\xf9\x9a\xe9\xed\xcb\xdf\xe6\xb2\xcfJ=o \xcd\xfaF\xa9\"䇔\xaa\xa6\xb4\xd4y\xf5\xb7f\xa1\xd2\xf6\xf2\xee4H\xde\xf4\xcb\xf4W\xb3\x8c\xe1*\x85j\x05,63|w\x11\x9eh`s\x98\x02\xfe'\x00\x00\xff\xff\xef\xf8\xa6>\x10\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcVM\x8f\xdb6\x10\xbd\xfbW\f\x92kd7(Z\x14\xbe\x05\xdb\x1e\x82&\xc5\"N\xf7N\x93#{j\x8ad\x87C9.\xfa\xe3\v\x92Ү-\xcb\xc9nQT\x17\xc3\xe4\xf0q>\u07bca\xd34\v\x15\xe8\x019\x92wkP\x81\xf0\x8b\xa0\xcb\xff\xe2\xf2\xf0S\\\x92_\xf5o\x17\arf\rw)\x8a\xef>a\xf4\x895\xfe\x8c-9\x12\xf2nѡ(\xa3D\xad\x17\x00\xca9/*/\xc7\xfc\x17@{'\xec\xadEnv薇\xb4\xc5m\"k\x90\v\xf8xu\xff\xdd\xf2\xed\x8f\xcb\x1f\x16\x00Nu\xb8\x86\xde\xdb\xd4at*Ľ\x17\xebu\xc5\\\xf6h\x91\xfd\x92\xfc\"\x06\xd4\xf9\x8a\x1d\xfb\x14\xd6\xf0\xb4Q!\x86\xeb\xab\xeb\x0f\x05m3\xa0}\x18Њ\x81\xa5(\xbf~\xc5\xe8\x03E)\x86\xc1&V\xf6\xa6g\xc5&\xee=\xcboO\xb77\xd0G[w\xc8\xed\x92U|\xeb\xfc\x02 j\x1fp\r\xe5xP\x1a\xcd\x02`\xc8O\x81k\xc6Լ\xad\x88z\x8f\x9d\xaa\xf7\x00\xf8\x80\xee\xdd\xfd\xfb\x87\xef7\x17\xcb\x00\x06\xa3f\nR\xb2<\x1f\"P\x04\x05\xa3'p\xdc##<\x94|B\x14\xcf\x18\a\xa7\x1fA\x01F\xff\xe3\xf2q1\xb0\x0f\xc8Bc\xf0\xf5;\xe3\xd7\xd9\xeaį\xbf\x9b\x8b=\x80\x1cJ=\x05&\x13\r#\xc8\x1e\xc7t\xa0\x19\xa2\a߂\xec)\x02c`\x8c\xe8*\xf5\xf2\xb2r\xe0\xb7\x7f\xa0\x96\xe5\x04z\x83\x9car\xad\x925\x99\x9f=\xb2\x00\xa3\xf6;G\x7f=bG\x10_.\xb5J0\n\x90\x13d\xa7,\xf4\xca&|\x03ʙ\tr\xa7N\xc0\x98\xef\x84\xe4\xce\xf0ʁ8\xf5\xe3\xa3g\x04r\xad_\xc3^$\xc4\xf5j\xb5#\x19\xbbN\xfb\xaeK\x8e\xe4\xb4*\rD\xdb$\x9e\xe3\xca`\x8fv\x15i\xd7(\xd6{\x12Ԓ\x18W*PS\x02q\xb5K:\xf3\x9a\x87>\x8d\x17\xd7\xca)S,\n\x93\u06ddm\x94.yAyr\xc3T\xd6T\xa8\x1a\xe2S\x15\xf2RNݧ_6\x9fa\xf4\xa4V\xaa\x16\xe5\xc9\xf4*/c}r6ɵ\xc8\xf5\\˾+\x98\xe8L\xf0\xe4\xa4\xfcі\xd0\tĴ\xedH2\r\xfeL\x18%\x97n\n{W\x94\t\xb6\b)\x18%h\xa6\x06\xef\x1dܩ\x0e흊\xf8?\xd7*W%6\xb9\bϪֹ\xdeN\x8dkz\xcf\x1bu\x90\xc9\x1b\xa5\x9dW\x84M@}\xd1x\x19\x85Z\x1a\x14\xa2\xf5i\x8b\x15\x10|;ý\x17\xb9\x9c?t\xa9\x9b#\xe2\xbb^\x91U[{-\t\r\xfc\xee\xd4\xcdݛş\xad\xe7\xd5b̏=\xb3\x06\xe1T\xb1\a\x96\r+\xff\x04\x00\x00\xff\xffNy\xc1Q\xa1\x0e\x00\x00"), diff --git a/examples/default-resource-modifier-cni.yaml b/examples/default-resource-modifier-cni.yaml new file mode 100644 index 000000000..352180f5a --- /dev/null +++ b/examples/default-resource-modifier-cni.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: default-restore-resource-modifiers + namespace: velero +data: + resource-modifiers.yaml: | + version: v1 + resourceModifierRules: + - conditions: + groupResource: pods + mergePatches: + - patchData: | + metadata: + annotations: + k8s.ovn.org/pod-networks: null + k8s.v1.cni.cncf.io/network-status: null + k8s.v1.cni.cncf.io/networks-status: null diff --git a/pkg/apis/velero/v1/restore_types.go b/pkg/apis/velero/v1/restore_types.go index c01686241..2ef791270 100644 --- a/pkg/apis/velero/v1/restore_types.go +++ b/pkg/apis/velero/v1/restore_types.go @@ -135,6 +135,14 @@ type RestoreSpec struct { // +nullable ResourcePolicy *corev1api.TypedLocalObjectReference `json:"resourcePolicy,omitempty"` + // SkipDefaultResourceModifier controls whether the server-configured default + // resource modifier is applied to this restore. + // When true, the default modifier is skipped even if configured on the server. + // Has no effect when a per-restore ResourceModifier is specified. + // +optional + // +nullable + SkipDefaultResourceModifier *bool `json:"skipDefaultResourceModifier,omitempty"` + // UploaderConfig specifies the configuration for the restore. // +optional // +nullable diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index 106beaa79..ffbbf0cf8 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -1427,6 +1427,11 @@ func (in *RestoreSpec) DeepCopyInto(out *RestoreSpec) { *out = new(corev1.TypedLocalObjectReference) (*in).DeepCopyInto(*out) } + if in.SkipDefaultResourceModifier != nil { + in, out := &in.SkipDefaultResourceModifier, &out.SkipDefaultResourceModifier + *out = new(bool) + **out = **in + } if in.UploaderConfig != nil { in, out := &in.UploaderConfig, &out.UploaderConfig *out = new(UploaderConfigForRestore) diff --git a/pkg/builder/restore_builder.go b/pkg/builder/restore_builder.go index 22e880a98..472e51a21 100644 --- a/pkg/builder/restore_builder.go +++ b/pkg/builder/restore_builder.go @@ -181,3 +181,9 @@ func (b *RestoreBuilder) ResourcePoliciesConfigmap(name string) *RestoreBuilder } return b } + +// SkipDefaultResourceModifier sets whether to skip the server default resource modifier. +func (b *RestoreBuilder) SkipDefaultResourceModifier(val bool) *RestoreBuilder { + b.object.Spec.SkipDefaultResourceModifier = &val + return b +} diff --git a/pkg/cmd/cli/install/install.go b/pkg/cmd/cli/install/install.go index 0df53eb32..67c9517da 100644 --- a/pkg/cmd/cli/install/install.go +++ b/pkg/cmd/cli/install/install.go @@ -42,60 +42,61 @@ import ( // Options collects all the options for installing Velero into a Kubernetes cluster. type Options struct { - Namespace string - Image string - BucketName string - Prefix string - ProviderName string - PodAnnotations flag.Map - PodLabels flag.Map - ServiceAccountAnnotations flag.Map - ServiceAccountName string - VeleroPodCPURequest string - VeleroPodMemRequest string - VeleroPodCPULimit string - VeleroPodMemLimit string - NodeAgentPodCPURequest string - NodeAgentPodMemRequest string - NodeAgentPodCPULimit string - NodeAgentPodMemLimit string - RestoreOnly bool - SecretFile string - NoSecret bool - DryRun bool - BackupStorageConfig flag.Map - VolumeSnapshotConfig flag.Map - UseNodeAgent bool - UseNodeAgentWindows bool - PrivilegedNodeAgent bool - Wait bool - UseVolumeSnapshots bool - DefaultRepoMaintenanceFrequency time.Duration - GarbageCollectionFrequency time.Duration - PodVolumeOperationTimeout time.Duration - Plugins flag.StringArray - NoDefaultBackupLocation bool - CRDsOnly bool - CACertFile string - Features string - DefaultVolumesToFsBackup bool - UploaderType string - DefaultSnapshotMoveData bool - CSISnapshotEarlyFrequentPolling bool - DisableInformerCache bool - ScheduleSkipImmediately bool - PodResources kubeutil.PodResources - KeepLatestMaintenanceJobs int - BackupRepoConfigMap string - RepoMaintenanceJobConfigMap string - NodeAgentConfigMap string - ItemBlockWorkerCount int - ConcurrentBackups int - NodeAgentDisableHostPath bool - kubeletRootDir string - Apply bool - ServerPriorityClassName string - NodeAgentPriorityClassName string + Namespace string + Image string + BucketName string + Prefix string + ProviderName string + PodAnnotations flag.Map + PodLabels flag.Map + ServiceAccountAnnotations flag.Map + ServiceAccountName string + VeleroPodCPURequest string + VeleroPodMemRequest string + VeleroPodCPULimit string + VeleroPodMemLimit string + NodeAgentPodCPURequest string + NodeAgentPodMemRequest string + NodeAgentPodCPULimit string + NodeAgentPodMemLimit string + RestoreOnly bool + SecretFile string + NoSecret bool + DryRun bool + BackupStorageConfig flag.Map + VolumeSnapshotConfig flag.Map + UseNodeAgent bool + UseNodeAgentWindows bool + PrivilegedNodeAgent bool + Wait bool + UseVolumeSnapshots bool + DefaultRepoMaintenanceFrequency time.Duration + GarbageCollectionFrequency time.Duration + PodVolumeOperationTimeout time.Duration + Plugins flag.StringArray + NoDefaultBackupLocation bool + CRDsOnly bool + CACertFile string + Features string + DefaultVolumesToFsBackup bool + UploaderType string + DefaultSnapshotMoveData bool + CSISnapshotEarlyFrequentPolling bool + DisableInformerCache bool + ScheduleSkipImmediately bool + PodResources kubeutil.PodResources + KeepLatestMaintenanceJobs int + BackupRepoConfigMap string + RepoMaintenanceJobConfigMap string + DefaultResourceModifierConfigMap string + NodeAgentConfigMap string + ItemBlockWorkerCount int + ConcurrentBackups int + NodeAgentDisableHostPath bool + kubeletRootDir string + Apply bool + ServerPriorityClassName string + NodeAgentPriorityClassName string } // BindFlags adds command line values to the options struct. @@ -189,6 +190,12 @@ func (o *Options) BindFlags(flags *pflag.FlagSet) { o.RepoMaintenanceJobConfigMap, "The name of ConfigMap containing repository maintenance Job configurations.", ) + flags.StringVar( + &o.DefaultResourceModifierConfigMap, + "default-resource-modifier-configmap", + o.DefaultResourceModifierConfigMap, + "The name of a ConfigMap in the Velero namespace containing default resource modifier rules applied to all restores.", + ) flags.StringVar( &o.NodeAgentConfigMap, "node-agent-configmap", @@ -298,49 +305,50 @@ func (o *Options) 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(), - ServiceAccountName: o.ServiceAccountName, - VeleroPodResources: veleroPodResources, - NodeAgentPodResources: nodeAgentPodResources, - SecretData: secretData, - RestoreOnly: o.RestoreOnly, - UseNodeAgent: o.UseNodeAgent, - UseNodeAgentWindows: o.UseNodeAgentWindows, - PrivilegedNodeAgent: o.PrivilegedNodeAgent, - UseVolumeSnapshots: o.UseVolumeSnapshots, - BSLConfig: o.BackupStorageConfig.Data(), - VSLConfig: o.VolumeSnapshotConfig.Data(), - DefaultRepoMaintenanceFrequency: o.DefaultRepoMaintenanceFrequency, - GarbageCollectionFrequency: o.GarbageCollectionFrequency, - PodVolumeOperationTimeout: o.PodVolumeOperationTimeout, - Plugins: o.Plugins, - NoDefaultBackupLocation: o.NoDefaultBackupLocation, - CACertData: caCertData, - Features: strings.Split(o.Features, ","), - DefaultVolumesToFsBackup: o.DefaultVolumesToFsBackup, - UploaderType: o.UploaderType, - DefaultSnapshotMoveData: o.DefaultSnapshotMoveData, - CSISnapshotEarlyFrequentPolling: o.CSISnapshotEarlyFrequentPolling, - DisableInformerCache: o.DisableInformerCache, - ScheduleSkipImmediately: o.ScheduleSkipImmediately, - PodResources: o.PodResources, - KeepLatestMaintenanceJobs: o.KeepLatestMaintenanceJobs, - BackupRepoConfigMap: o.BackupRepoConfigMap, - RepoMaintenanceJobConfigMap: o.RepoMaintenanceJobConfigMap, - NodeAgentConfigMap: o.NodeAgentConfigMap, - ItemBlockWorkerCount: o.ItemBlockWorkerCount, - ConcurrentBackups: o.ConcurrentBackups, - KubeletRootDir: o.kubeletRootDir, - NodeAgentDisableHostPath: o.NodeAgentDisableHostPath, - ServerPriorityClassName: o.ServerPriorityClassName, - NodeAgentPriorityClassName: o.NodeAgentPriorityClassName, + 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(), + ServiceAccountName: o.ServiceAccountName, + VeleroPodResources: veleroPodResources, + NodeAgentPodResources: nodeAgentPodResources, + SecretData: secretData, + RestoreOnly: o.RestoreOnly, + UseNodeAgent: o.UseNodeAgent, + UseNodeAgentWindows: o.UseNodeAgentWindows, + PrivilegedNodeAgent: o.PrivilegedNodeAgent, + UseVolumeSnapshots: o.UseVolumeSnapshots, + BSLConfig: o.BackupStorageConfig.Data(), + VSLConfig: o.VolumeSnapshotConfig.Data(), + DefaultRepoMaintenanceFrequency: o.DefaultRepoMaintenanceFrequency, + GarbageCollectionFrequency: o.GarbageCollectionFrequency, + PodVolumeOperationTimeout: o.PodVolumeOperationTimeout, + Plugins: o.Plugins, + NoDefaultBackupLocation: o.NoDefaultBackupLocation, + CACertData: caCertData, + Features: strings.Split(o.Features, ","), + DefaultVolumesToFsBackup: o.DefaultVolumesToFsBackup, + UploaderType: o.UploaderType, + DefaultSnapshotMoveData: o.DefaultSnapshotMoveData, + CSISnapshotEarlyFrequentPolling: o.CSISnapshotEarlyFrequentPolling, + DisableInformerCache: o.DisableInformerCache, + ScheduleSkipImmediately: o.ScheduleSkipImmediately, + PodResources: o.PodResources, + KeepLatestMaintenanceJobs: o.KeepLatestMaintenanceJobs, + BackupRepoConfigMap: o.BackupRepoConfigMap, + RepoMaintenanceJobConfigMap: o.RepoMaintenanceJobConfigMap, + DefaultResourceModifierConfigMap: o.DefaultResourceModifierConfigMap, + NodeAgentConfigMap: o.NodeAgentConfigMap, + ItemBlockWorkerCount: o.ItemBlockWorkerCount, + ConcurrentBackups: o.ConcurrentBackups, + KubeletRootDir: o.kubeletRootDir, + NodeAgentDisableHostPath: o.NodeAgentDisableHostPath, + ServerPriorityClassName: o.ServerPriorityClassName, + NodeAgentPriorityClassName: o.NodeAgentPriorityClassName, }, nil } diff --git a/pkg/cmd/cli/restore/create.go b/pkg/cmd/cli/restore/create.go index 3f59b6a6b..c76097176 100644 --- a/pkg/cmd/cli/restore/create.go +++ b/pkg/cmd/cli/restore/create.go @@ -85,32 +85,33 @@ Notes: } type CreateOptions struct { - BackupName string - ScheduleName string - RestoreName string - RestoreVolumes flag.OptionalBool - PreserveNodePorts flag.OptionalBool - Labels flag.Map - Annotations flag.Map - IncludeNamespaces flag.StringArray - ExcludeNamespaces flag.StringArray - ExistingResourcePolicy string - IncludeResources flag.StringArray - ExcludeResources flag.StringArray - StatusIncludeResources flag.StringArray - StatusExcludeResources flag.StringArray - NamespaceMappings flag.Map - Selector flag.LabelSelector - OrSelector flag.OrLabelSelector - IncludeClusterResources flag.OptionalBool - Wait bool - AllowPartiallyFailed flag.OptionalBool - ItemOperationTimeout time.Duration - ResourceModifierConfigMap string - ResourcePoliciesConfigMap string - WriteSparseFiles flag.OptionalBool - ParallelFilesDownload int - client kbclient.WithWatch + BackupName string + ScheduleName string + RestoreName string + RestoreVolumes flag.OptionalBool + PreserveNodePorts flag.OptionalBool + Labels flag.Map + Annotations flag.Map + IncludeNamespaces flag.StringArray + ExcludeNamespaces flag.StringArray + ExistingResourcePolicy string + IncludeResources flag.StringArray + ExcludeResources flag.StringArray + StatusIncludeResources flag.StringArray + StatusExcludeResources flag.StringArray + NamespaceMappings flag.Map + Selector flag.LabelSelector + OrSelector flag.OrLabelSelector + IncludeClusterResources flag.OptionalBool + Wait bool + AllowPartiallyFailed flag.OptionalBool + ItemOperationTimeout time.Duration + ResourceModifierConfigMap string + ResourcePoliciesConfigMap string + SkipDefaultResourceModifier bool + WriteSparseFiles flag.OptionalBool + ParallelFilesDownload int + client kbclient.WithWatch } func NewCreateOptions() *CreateOptions { @@ -164,6 +165,8 @@ func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { flags.StringVar(&o.ResourcePoliciesConfigMap, "resource-policies-configmap", "", "Reference to the ConfigMap containing restore resource filter policies") + flags.BoolVar(&o.SkipDefaultResourceModifier, "skip-default-resource-modifier", false, "Skip applying the server-configured default resource modifier for this restore") + f = flags.VarPF(&o.WriteSparseFiles, "write-sparse-files", "", "Whether to write sparse files during restoring volumes") f.NoOptDefVal = cmd.TRUE @@ -362,6 +365,10 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { }, } + if o.SkipDefaultResourceModifier { + restore.Spec.SkipDefaultResourceModifier = boolptr.True() + } + if len([]string(o.StatusIncludeResources)) > 0 { restore.Spec.RestoreStatus = &api.RestoreStatusSpec{ IncludedResources: o.StatusIncludeResources, diff --git a/pkg/cmd/cli/restore/create_test.go b/pkg/cmd/cli/restore/create_test.go index 9a6a92608..643340e22 100644 --- a/pkg/cmd/cli/restore/create_test.go +++ b/pkg/cmd/cli/restore/create_test.go @@ -105,6 +105,7 @@ func TestCreateCommand(t *testing.T) { flags.Parse([]string{"--item-operation-timeout", itemOperationTimeout}) flags.Parse([]string{"--resource-modifier-configmap", resourceModifierConfigMap}) flags.Parse([]string{"--resource-policies-configmap", ResourcePoliciesConfigMap}) + flags.Parse([]string{"--skip-default-resource-modifier"}) flags.Parse([]string{"--write-sparse-files", writeSparseFiles}) flags.Parse([]string{"--parallel-files-download", "2"}) client := velerotest.NewFakeControllerRuntimeClient(t).(kbclient.WithWatch) @@ -145,6 +146,7 @@ func TestCreateCommand(t *testing.T) { require.Equal(t, itemOperationTimeout, o.ItemOperationTimeout.String()) require.Equal(t, resourceModifierConfigMap, o.ResourceModifierConfigMap) require.Equal(t, ResourcePoliciesConfigMap, o.ResourcePoliciesConfigMap) + require.True(t, o.SkipDefaultResourceModifier) require.Equal(t, writeSparseFiles, o.WriteSparseFiles.String()) require.Equal(t, parallel, o.ParallelFilesDownload) }) diff --git a/pkg/cmd/server/config/config.go b/pkg/cmd/server/config/config.go index c8080da21..08b58a1bd 100644 --- a/pkg/cmd/server/config/config.go +++ b/pkg/cmd/server/config/config.go @@ -182,6 +182,7 @@ type Config struct { ItemBlockWorkerCount int ConcurrentBackups int GlobalBackupVolumePoliciesConfigMap string + DefaultResourceModifierConfigMap string } func GetDefaultConfig() *Config { @@ -282,4 +283,10 @@ func (c *Config) BindFlags(flags *pflag.FlagSet) { c.GlobalBackupVolumePoliciesConfigMap, "The name of a ConfigMap in the Velero install namespace holding global backup volume policies that are merged into every backup. Optional.", ) + flags.StringVar( + &c.DefaultResourceModifierConfigMap, + "default-resource-modifier-configmap", + c.DefaultResourceModifierConfigMap, + "The name of a ConfigMap in the Velero namespace containing default resource modifier rules applied to all restores. Ignored when a per-restore resource modifier is specified.", + ) } diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 83627f9d1..7aff5e946 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -881,6 +881,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.config.DisableInformerCache, s.crClient, s.config.ResourceTimeout, + s.config.DefaultResourceModifierConfigMap, ) if err = r.SetupWithManager(s.mgr); err != nil { diff --git a/pkg/cmd/util/output/restore_describer.go b/pkg/cmd/util/output/restore_describer.go index c33da9f69..e94b2dedd 100644 --- a/pkg/cmd/util/output/restore_describer.go +++ b/pkg/cmd/util/output/restore_describer.go @@ -219,6 +219,10 @@ func DescribeRestore( DescribeResourceModifier(d, restore.Spec.ResourceModifier) } + if boolptr.IsSetToTrue(restore.Spec.SkipDefaultResourceModifier) { + d.Printf("Skip Default Resource Modifier:\ttrue\n") + } + if restore.Spec.ResourcePolicy != nil { d.Println() DescribeResourcePolicies(d, restore.Spec.ResourcePolicy) diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index 5b055bc6c..e4eb68144 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -55,6 +55,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt" "github.com/vmware-tanzu/velero/pkg/plugin/framework" pkgrestore "github.com/vmware-tanzu/velero/pkg/restore" + "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/util/collections" kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube" "github.com/vmware-tanzu/velero/pkg/util/logging" @@ -109,10 +110,11 @@ type restoreReconciler struct { defaultItemOperationTimeout time.Duration disableInformerCache bool - newPluginManager func(logger logrus.FieldLogger) clientmgmt.Manager - backupStoreGetter persistence.ObjectBackupStoreGetter - globalCrClient client.Client - resourceTimeout time.Duration + newPluginManager func(logger logrus.FieldLogger) clientmgmt.Manager + backupStoreGetter persistence.ObjectBackupStoreGetter + globalCrClient client.Client + resourceTimeout time.Duration + defaultResourceModifierConfigMap string } type backupInfo struct { @@ -135,6 +137,7 @@ func NewRestoreReconciler( disableInformerCache bool, globalCrClient client.Client, resourceTimeout time.Duration, + defaultResourceModifierConfigMap string, ) *restoreReconciler { r := &restoreReconciler{ ctx: ctx, @@ -154,8 +157,9 @@ func NewRestoreReconciler( newPluginManager: newPluginManager, backupStoreGetter: backupStoreGetter, - globalCrClient: globalCrClient, - resourceTimeout: resourceTimeout, + globalCrClient: globalCrClient, + resourceTimeout: resourceTimeout, + defaultResourceModifierConfigMap: defaultResourceModifierConfigMap, } // Move the periodical backup and restore metrics computing logic from controllers to here. @@ -431,27 +435,72 @@ func (r *restoreReconciler) validateAndComplete(ctx context.Context, restore *ap } var resourceModifiers *resourcemodifiers.ResourceModifiers - if restore.Spec.ResourceModifier != nil && strings.EqualFold(restore.Spec.ResourceModifier.Kind, resourcemodifiers.ConfigmapRefType) { - ResourceModifierConfigMap := &corev1api.ConfigMap{} - err := r.kbClient.Get(ctx, client.ObjectKey{Namespace: restore.Namespace, Name: restore.Spec.ResourceModifier.Name}, ResourceModifierConfigMap) - if err != nil { - restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, fmt.Sprintf("failed to get resource modifiers configmap %s/%s", restore.Namespace, restore.Spec.ResourceModifier.Name)) - return backupInfo{}, nil, nil + if restore.Spec.ResourceModifier != nil { + if strings.EqualFold(restore.Spec.ResourceModifier.Kind, resourcemodifiers.ConfigmapRefType) { + resourceModifiers = r.loadResourceModifierConfigMap(ctx, restore, restore.Spec.ResourceModifier.Name, false) + if resourceModifiers == nil && len(restore.Status.ValidationErrors) > 0 { + return backupInfo{}, nil, nil + } + } else { + r.logger.Warnf("Unsupported resource modifier kind %q, only %q is supported", restore.Spec.ResourceModifier.Kind, resourcemodifiers.ConfigmapRefType) } - resourceModifiers, err = resourcemodifiers.GetResourceModifiersFromConfig(ResourceModifierConfigMap) - if err != nil { - restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, errors.Wrapf(err, "Error in parsing resource modifiers provided in configmap %s/%s", restore.Namespace, restore.Spec.ResourceModifier.Name).Error()) - return backupInfo{}, nil, nil - } else if err = resourceModifiers.Validate(); err != nil { - restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, errors.Wrapf(err, "Validation error in resource modifiers provided in configmap %s/%s", restore.Namespace, restore.Spec.ResourceModifier.Name).Error()) - return backupInfo{}, nil, nil + } else if r.defaultResourceModifierConfigMap != "" { + if boolptr.IsSetToTrue(restore.Spec.SkipDefaultResourceModifier) { + r.logger.Infof("Skipping default resource modifier configmap %s/%s as SkipDefaultResourceModifier is set", restore.Namespace, r.defaultResourceModifierConfigMap) + } else { + resourceModifiers = r.loadResourceModifierConfigMap(ctx, restore, r.defaultResourceModifierConfigMap, true) } - r.logger.Infof("Retrieved Resource modifiers provided in configmap %s/%s", restore.Namespace, restore.Spec.ResourceModifier.Name) } return info, resourceModifiers, restoreResPolicies } +// loadResourceModifierConfigMap loads and validates a resource modifier ConfigMap. +// When isDefault is true, errors are non-fatal (logged as warnings, returns nil). +// When isDefault is false, errors are added to restore.Status.ValidationErrors. +func (r *restoreReconciler) loadResourceModifierConfigMap( + ctx context.Context, restore *api.Restore, cmName string, isDefault bool, +) *resourcemodifiers.ResourceModifiers { + cm := &corev1api.ConfigMap{} + if err := r.kbClient.Get(ctx, client.ObjectKey{Namespace: restore.Namespace, Name: cmName}, cm); err != nil { + if isDefault { + r.logger.WithError(err).Warnf("Failed to retrieve default resource modifier configmap %s/%s, skipping", restore.Namespace, cmName) + return nil + } + restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, + fmt.Sprintf("failed to get resource modifiers configmap %s/%s: %v", restore.Namespace, cmName, err)) + return nil + } + + modifiers, err := resourcemodifiers.GetResourceModifiersFromConfig(cm) + if err != nil { + if isDefault { + r.logger.WithError(err).Warnf("Error parsing default resource modifier configmap %s/%s, skipping", restore.Namespace, cmName) + return nil + } + restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, + errors.Wrapf(err, "Error in parsing resource modifiers provided in configmap %s/%s", restore.Namespace, cmName).Error()) + return nil + } + + if err = modifiers.Validate(); err != nil { + if isDefault { + r.logger.WithError(err).Warnf("Validation error in default resource modifier configmap %s/%s, skipping", restore.Namespace, cmName) + return nil + } + restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, + errors.Wrapf(err, "Validation error in resource modifiers provided in configmap %s/%s", restore.Namespace, cmName).Error()) + return nil + } + + source := "per-restore" + if isDefault { + source = "default" + } + r.logger.Infof("Retrieved %s resource modifiers from configmap %s/%s", source, restore.Namespace, cmName) + return modifiers +} + // backupXorScheduleProvided returns true if exactly one of BackupName and // ScheduleName are non-empty for the restore, or false otherwise. func backupXorScheduleProvided(restore *api.Restore) bool { diff --git a/pkg/controller/restore_controller_test.go b/pkg/controller/restore_controller_test.go index 6a2f4d8d1..738ad43db 100644 --- a/pkg/controller/restore_controller_test.go +++ b/pkg/controller/restore_controller_test.go @@ -116,6 +116,7 @@ func TestFetchBackupInfo(t *testing.T) { false, fakeGlobalClient, 10*time.Minute, + "", ) if test.backupStoreError == nil { @@ -197,6 +198,7 @@ func TestProcessQueueItemSkips(t *testing.T) { false, fakeGlobalClient, 10*time.Minute, + "", ) _, err := r.Reconcile(t.Context(), ctrl.Request{NamespacedName: types.NamespacedName{ @@ -579,6 +581,7 @@ func TestRestoreReconcile(t *testing.T) { false, fakeGlobalClient, 10*time.Minute, + "", ) r.clock = clocktesting.NewFakeClock(now) @@ -767,6 +770,7 @@ func TestValidateAndCompleteWhenScheduleNameSpecified(t *testing.T) { false, fakeGlobalClient, 10*time.Minute, + "", ) restore := &velerov1api.Restore{ @@ -863,6 +867,7 @@ func TestValidateAndCompleteWithResourcePolicySpecified(t *testing.T) { false, fakeGlobalClient, 10*time.Minute, + "", ) restore := &velerov1api.Restore{ @@ -992,6 +997,7 @@ func TestValidateAndCompleteWithResourceModifierSpecified(t *testing.T) { false, fakeGlobalClient, 10*time.Minute, + "", ) restore := &velerov1api.Restore{ @@ -1110,6 +1116,184 @@ func TestValidateAndCompleteWithResourceModifierSpecified(t *testing.T) { assert.Contains(t, restore3.Status.ValidationErrors[0], "Validation error in resource modifiers provided in configmap") } +func TestValidateAndCompleteWithDefaultResourceModifier(t *testing.T) { + formatFlag := logging.FormatText + + validCMData := map[string]string{ + "modifiers.yaml": "version: v1\nresourceModifierRules:\n- conditions:\n groupResource: pods\n mergePatches:\n - patchData: |\n metadata:\n annotations:\n k8s.ovn.org/pod-networks: null\n", + } + + setupReconciler := func(t *testing.T, defaultCM string) *restoreReconciler { + t.Helper() + fakeClient := velerotest.NewFakeControllerRuntimeClient(t) + fakeGlobalClient := velerotest.NewFakeControllerRuntimeClient(t) + pluginManager := &pluginmocks.Manager{} + backupStore := &persistencemocks.BackupStore{} + + r := NewRestoreReconciler( + t.Context(), + velerov1api.DefaultNamespace, + nil, + fakeClient, + velerotest.NewLogger(), + logrus.DebugLevel, + func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }, + NewFakeSingleObjectBackupStoreGetter(backupStore), + metrics.NewServerMetrics(), + formatFlag, + 60*time.Minute, + false, + fakeGlobalClient, + 10*time.Minute, + defaultCM, + ) + + location := builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Phase(velerov1api.BackupStorageLocationPhaseAvailable).Result() + require.NoError(t, r.kbClient.Create(t.Context(), location)) + require.NoError(t, r.kbClient.Create(t.Context(), + defaultBackup().ObjectMeta(builder.WithName("backup-1")).StorageLocation("default").Phase(velerov1api.BackupPhaseCompleted).Result(), + )) + return r + } + + newRestore := func(perRestoreCM string, skip *bool) *velerov1api.Restore { + restore := &velerov1api.Restore{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1api.DefaultNamespace, + Name: "restore-1", + }, + Spec: velerov1api.RestoreSpec{ + BackupName: "backup-1", + SkipDefaultResourceModifier: skip, + }, + } + if perRestoreCM != "" { + restore.Spec.ResourceModifier = &corev1api.TypedLocalObjectReference{ + Kind: resourcemodifiers.ConfigmapRefType, + Name: perRestoreCM, + } + } + return restore + } + + t.Run("default modifier applied when no per-restore modifier", func(t *testing.T) { + r := setupReconciler(t, "default-rm") + require.NoError(t, r.kbClient.Create(t.Context(), &corev1api.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: "default-rm", Namespace: velerov1api.DefaultNamespace}, + Data: validCMData, + })) + + restore := newRestore("", nil) + _, rm, _ := r.validateAndComplete(t.Context(), restore) + assert.NotNil(t, rm) + assert.Empty(t, restore.Status.ValidationErrors) + }) + + t.Run("per-restore modifier takes exclusive precedence over default", func(t *testing.T) { + // Default ConfigMap does NOT exist, but per-restore does. + // If default were applied, it would fail. Per-restore should succeed. + r := setupReconciler(t, "nonexistent-default") + require.NoError(t, r.kbClient.Create(t.Context(), &corev1api.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: "per-restore-rm", Namespace: velerov1api.DefaultNamespace}, + Data: validCMData, + })) + + restore := newRestore("per-restore-rm", nil) + _, rm, _ := r.validateAndComplete(t.Context(), restore) + assert.NotNil(t, rm) + assert.Empty(t, restore.Status.ValidationErrors) + }) + + t.Run("skip default modifier when SkipDefaultResourceModifier is true", func(t *testing.T) { + r := setupReconciler(t, "default-rm") + require.NoError(t, r.kbClient.Create(t.Context(), &corev1api.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: "default-rm", Namespace: velerov1api.DefaultNamespace}, + Data: validCMData, + })) + + skipTrue := true + restore := newRestore("", &skipTrue) + _, rm, _ := r.validateAndComplete(t.Context(), restore) + assert.Nil(t, rm) + assert.Empty(t, restore.Status.ValidationErrors) + }) + + t.Run("default modifier with invalid data is non-fatal", func(t *testing.T) { + r := setupReconciler(t, "invalid-default") + require.NoError(t, r.kbClient.Create(t.Context(), &corev1api.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: "invalid-default", Namespace: velerov1api.DefaultNamespace}, + Data: map[string]string{ + "modifiers.yaml": "not-valid-yaml: [", + }, + })) + + restore := newRestore("", nil) + _, rm, _ := r.validateAndComplete(t.Context(), restore) + assert.Nil(t, rm) + assert.Empty(t, restore.Status.ValidationErrors) + }) + + t.Run("default modifier missing is non-fatal", func(t *testing.T) { + r := setupReconciler(t, "nonexistent-cm") + + restore := newRestore("", nil) + _, rm, _ := r.validateAndComplete(t.Context(), restore) + assert.Nil(t, rm) + assert.Empty(t, restore.Status.ValidationErrors) + }) + + t.Run("per-restore modifier missing is fatal", func(t *testing.T) { + r := setupReconciler(t, "") + + restore := newRestore("nonexistent-cm", nil) + _, rm, _ := r.validateAndComplete(t.Context(), restore) + assert.Nil(t, rm) + assert.NotEmpty(t, restore.Status.ValidationErrors) + assert.Contains(t, restore.Status.ValidationErrors[0], "failed to get resource modifiers configmap") + }) + + t.Run("no default configured and no per-restore modifier", func(t *testing.T) { + r := setupReconciler(t, "") + + restore := newRestore("", nil) + _, rm, _ := r.validateAndComplete(t.Context(), restore) + assert.Nil(t, rm) + assert.Empty(t, restore.Status.ValidationErrors) + }) + + t.Run("unsupported resource modifier kind does not apply default", func(t *testing.T) { + r := setupReconciler(t, "default-rm") + require.NoError(t, r.kbClient.Create(t.Context(), &corev1api.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: "default-rm", Namespace: velerov1api.DefaultNamespace}, + Data: validCMData, + })) + + restore := newRestore("", nil) + restore.Spec.ResourceModifier = &corev1api.TypedLocalObjectReference{ + Kind: "Secret", + Name: "some-secret", + } + _, rm, _ := r.validateAndComplete(t.Context(), restore) + assert.Nil(t, rm) + assert.Empty(t, restore.Status.ValidationErrors) + }) + + t.Run("default modifier validation failure is non-fatal", func(t *testing.T) { + r := setupReconciler(t, "invalid-validation") + require.NoError(t, r.kbClient.Create(t.Context(), &corev1api.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: "invalid-validation", Namespace: velerov1api.DefaultNamespace}, + Data: map[string]string{ + "modifiers.yaml": "version: v1\nresourceModifierRules:\n- conditions:\n groupResource: pods\n patches:\n - operation: invalid\n path: \"/spec\"\n value: \"test\"\n", + }, + })) + + restore := newRestore("", nil) + _, rm, _ := r.validateAndComplete(t.Context(), restore) + assert.Nil(t, rm) + assert.Empty(t, restore.Status.ValidationErrors) + }) +} + func TestBackupXorScheduleProvided(t *testing.T) { r := &velerov1api.Restore{} assert.False(t, backupXorScheduleProvided(r)) diff --git a/pkg/install/deployment.go b/pkg/install/deployment.go index e9474f1fe..6bea8b0be 100644 --- a/pkg/install/deployment.go +++ b/pkg/install/deployment.go @@ -34,37 +34,38 @@ import ( type podTemplateOption func(*podTemplateConfig) type podTemplateConfig struct { - image string - envVars []corev1api.EnvVar - restoreOnly bool - annotations map[string]string - labels map[string]string - resources corev1api.ResourceRequirements - withSecret bool - defaultRepoMaintenanceFrequency time.Duration - garbageCollectionFrequency time.Duration - podVolumeOperationTimeout time.Duration - plugins []string - features []string - defaultVolumesToFsBackup bool - serviceAccountName string - uploaderType string - defaultSnapshotMoveData bool - csiSnapshotEarlyFrequentPolling bool - privilegedNodeAgent bool - disableInformerCache bool - scheduleSkipImmediately bool - podResources kube.PodResources - keepLatestMaintenanceJobs int - backupRepoConfigMap string - repoMaintenanceJobConfigMap string - nodeAgentConfigMap string - itemBlockWorkerCount int - concurrentBackups int - forWindows bool - kubeletRootDir string - nodeAgentDisableHostPath bool - priorityClassName string + image string + envVars []corev1api.EnvVar + restoreOnly bool + annotations map[string]string + labels map[string]string + resources corev1api.ResourceRequirements + withSecret bool + defaultRepoMaintenanceFrequency time.Duration + garbageCollectionFrequency time.Duration + podVolumeOperationTimeout time.Duration + plugins []string + features []string + defaultVolumesToFsBackup bool + serviceAccountName string + uploaderType string + defaultSnapshotMoveData bool + csiSnapshotEarlyFrequentPolling bool + privilegedNodeAgent bool + disableInformerCache bool + scheduleSkipImmediately bool + podResources kube.PodResources + keepLatestMaintenanceJobs int + backupRepoConfigMap string + repoMaintenanceJobConfigMap string + defaultResourceModifierConfigMap string + nodeAgentConfigMap string + itemBlockWorkerCount int + concurrentBackups int + forWindows bool + kubeletRootDir string + nodeAgentDisableHostPath bool + priorityClassName string } func WithImage(image string) podTemplateOption { @@ -229,6 +230,12 @@ func WithRepoMaintenanceJobConfigMap(repoMaintenanceJobConfigMap string) podTemp } } +func WithDefaultResourceModifierConfigMap(name string) podTemplateOption { + return func(c *podTemplateConfig) { + c.defaultResourceModifierConfigMap = name + } +} + func WithItemBlockWorkerCount(itemBlockWorkerCount int) podTemplateOption { return func(c *podTemplateConfig) { c.itemBlockWorkerCount = itemBlockWorkerCount @@ -350,6 +357,10 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1api.Deployme args = append(args, fmt.Sprintf("--repo-maintenance-job-configmap=%s", c.repoMaintenanceJobConfigMap)) } + if len(c.defaultResourceModifierConfigMap) > 0 { + args = append(args, fmt.Sprintf("--default-resource-modifier-configmap=%s", c.defaultResourceModifierConfigMap)) + } + if c.itemBlockWorkerCount > 0 { args = append(args, fmt.Sprintf("--item-block-worker-count=%d", c.itemBlockWorkerCount)) } diff --git a/pkg/install/deployment_test.go b/pkg/install/deployment_test.go index 0cfcb65dd..6e9ff6ec5 100644 --- a/pkg/install/deployment_test.go +++ b/pkg/install/deployment_test.go @@ -109,6 +109,10 @@ func TestDeployment(t *testing.T) { assert.Len(t, deploy.Spec.Template.Spec.Containers[0].Args, 2) assert.Equal(t, "--repo-maintenance-job-configmap=test-repo-maintenance-config", deploy.Spec.Template.Spec.Containers[0].Args[1]) + deploy = Deployment("velero", WithDefaultResourceModifierConfigMap("default-restore-modifiers")) + assert.Len(t, deploy.Spec.Template.Spec.Containers[0].Args, 2) + assert.Equal(t, "--default-resource-modifier-configmap=default-restore-modifiers", deploy.Spec.Template.Spec.Containers[0].Args[1]) + assert.Equal(t, &corev1api.Affinity{ NodeAffinity: &corev1api.NodeAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: &corev1api.NodeSelector{ diff --git a/pkg/install/resources.go b/pkg/install/resources.go index c4ec6f1bc..9f9543300 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -234,49 +234,50 @@ 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 - ServiceAccountName string - VeleroPodResources corev1api.ResourceRequirements - NodeAgentPodResources corev1api.ResourceRequirements - SecretData []byte - RestoreOnly bool - UseNodeAgent bool - UseNodeAgentWindows bool - PrivilegedNodeAgent bool - UseVolumeSnapshots bool - BSLConfig map[string]string - VSLConfig map[string]string - DefaultRepoMaintenanceFrequency time.Duration - GarbageCollectionFrequency time.Duration - PodVolumeOperationTimeout time.Duration - Plugins []string - NoDefaultBackupLocation bool - CACertData []byte - Features []string - DefaultVolumesToFsBackup bool - UploaderType string - DefaultSnapshotMoveData bool - CSISnapshotEarlyFrequentPolling bool - DisableInformerCache bool - ScheduleSkipImmediately bool - PodResources kube.PodResources - KeepLatestMaintenanceJobs int - BackupRepoConfigMap string - RepoMaintenanceJobConfigMap string - NodeAgentConfigMap string - ItemBlockWorkerCount int - ConcurrentBackups int - KubeletRootDir string - NodeAgentDisableHostPath bool - ServerPriorityClassName string - NodeAgentPriorityClassName string + Namespace string + Image string + ProviderName string + Bucket string + Prefix string + PodAnnotations map[string]string + PodLabels map[string]string + ServiceAccountAnnotations map[string]string + ServiceAccountName string + VeleroPodResources corev1api.ResourceRequirements + NodeAgentPodResources corev1api.ResourceRequirements + SecretData []byte + RestoreOnly bool + UseNodeAgent bool + UseNodeAgentWindows bool + PrivilegedNodeAgent bool + UseVolumeSnapshots bool + BSLConfig map[string]string + VSLConfig map[string]string + DefaultRepoMaintenanceFrequency time.Duration + GarbageCollectionFrequency time.Duration + PodVolumeOperationTimeout time.Duration + Plugins []string + NoDefaultBackupLocation bool + CACertData []byte + Features []string + DefaultVolumesToFsBackup bool + UploaderType string + DefaultSnapshotMoveData bool + CSISnapshotEarlyFrequentPolling bool + DisableInformerCache bool + ScheduleSkipImmediately bool + PodResources kube.PodResources + KeepLatestMaintenanceJobs int + BackupRepoConfigMap string + RepoMaintenanceJobConfigMap string + DefaultResourceModifierConfigMap string + NodeAgentConfigMap string + ItemBlockWorkerCount int + ConcurrentBackups int + KubeletRootDir string + NodeAgentDisableHostPath bool + ServerPriorityClassName string + NodeAgentPriorityClassName string } func AllCRDs() *unstructured.UnstructuredList { @@ -407,6 +408,10 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { deployOpts = append(deployOpts, WithRepoMaintenanceJobConfigMap(o.RepoMaintenanceJobConfigMap)) } + if len(o.DefaultResourceModifierConfigMap) > 0 { + deployOpts = append(deployOpts, WithDefaultResourceModifierConfigMap(o.DefaultResourceModifierConfigMap)) + } + deploy := Deployment(o.Namespace, deployOpts...) if err := appendUnstructured(resources, deploy); err != nil { diff --git a/pkg/install/resources_test.go b/pkg/install/resources_test.go index bafa3a684..29b99ed41 100644 --- a/pkg/install/resources_test.go +++ b/pkg/install/resources_test.go @@ -118,6 +118,32 @@ func TestAllResources(t *testing.T) { assert.Len(t, ds, 2) } +func TestAllResourcesWithDefaultResourceModifierConfigMap(t *testing.T) { + option := &VeleroOptions{ + Namespace: "velero", + SecretData: []byte{'a'}, + DefaultResourceModifierConfigMap: "default-rm", + } + list := AllResources(option) + + for _, item := range list.Items { + if item.GetKind() == "Deployment" && item.GetName() == "velero" { + containers, _, _ := unstructured.NestedSlice(item.Object, "spec", "template", "spec", "containers") + args, _, _ := unstructured.NestedStringSlice(containers[0].(map[string]any), "args") + found := false + for _, arg := range args { + if arg == "--default-resource-modifier-configmap=default-rm" { + found = true + break + } + } + assert.True(t, found, "expected --default-resource-modifier-configmap=default-rm in deployment args") + return + } + } + t.Fatal("velero deployment not found in AllResources output") +} + func TestAllResourcesWithPriorityClassName(t *testing.T) { testCases := []struct { name string diff --git a/site/content/docs/main/customize-installation.md b/site/content/docs/main/customize-installation.md index e42d6a3f8..e9561eea9 100644 --- a/site/content/docs/main/customize-installation.md +++ b/site/content/docs/main/customize-installation.md @@ -501,6 +501,7 @@ By far, `velero install` supports the following parameters to specify the extern * --backup-repository-configmap: [backup repository configuration document][15] * --node-agent-configmap: [node-agent concurrency configuration document][16], and there are some other documents specify other parts of node-agent-config. * --repo-maintenance-job-configmap: [repository maintenance configuration document][17] +* --default-resource-modifier-configmap: [default restore resource modifier document][18]. When set, the referenced ConfigMap's resource modifier rules apply automatically to all restores that don't specify a per-restore modifier. From v1.17, Velero adds verification for the ConfigMaps in CLI and server side, which means `velero install` CLI will fail and velero server and node-agent pod will exit if the specified ConfigMaps don't exist or are invalid. @@ -539,3 +540,4 @@ The new workflow is: [15]: backup-repository-configuration.md [16]: node-agent-concurrency.md [17]: repository-maintenance.md +[18]: restore-resource-modifiers.md#default-resource-modifiers diff --git a/site/content/docs/main/restore-resource-modifiers.md b/site/content/docs/main/restore-resource-modifiers.md index 0c1f2f217..39248ad30 100644 --- a/site/content/docs/main/restore-resource-modifiers.md +++ b/site/content/docs/main/restore-resource-modifiers.md @@ -184,4 +184,47 @@ resourceModifierRules: ### Wildcard Support for GroupResource The user can specify a wildcard for groupResource in the conditions' struct. This will allow the user to apply the patches for all the resources of a particular group or all resources in all groups. For example, `*.apps` will apply to all the resources in the `apps` group, `*` will apply to all the resources in core group, `*.*` will apply to all the resources in all groups. -- If both `*.groupName` and `namespaces` are specified, the patches will be applied to all the namespaced resources in this group in the specified namespaces and all the cluster resources in this group. \ No newline at end of file +- If both `*.groupName` and `namespaces` are specified, the patches will be applied to all the namespaced resources in this group in the specified namespaces and all the cluster resources in this group. + +## Default Resource Modifiers + +Velero supports a server-level default resource modifier that applies automatically to all restores without requiring per-restore configuration. +This is useful for common transformations like stripping stale CNI annotations that can break workloads after restore. + +### Configuration + +1. Create a ConfigMap in the Velero namespace with your default resource modifier rules: + +```bash +kubectl apply -f examples/default-resource-modifier-cni.yaml +``` + +2. Configure the Velero server to use it, either during install: + +```bash +velero install --default-resource-modifier-configmap=default-restore-resource-modifiers ... +``` + +Or by editing an existing deployment: + +```bash +kubectl -n velero edit deploy velero +# Add to the server args: --default-resource-modifier-configmap=default-restore-resource-modifiers +``` + +### Precedence + +When a per-restore modifier is specified via `--resource-modifier-configmap`, it takes exclusive precedence and the default is not applied. + +### Opt-out + +To skip the default modifier for a specific restore without specifying a per-restore modifier: + +```bash +velero restore create --from-backup my-backup --skip-default-resource-modifier +``` + +### Error Handling + +If the default ConfigMap is missing or contains invalid data, Velero logs a warning and proceeds with the restore. +Per-restore modifier errors remain fatal and cause the restore to fail validation. \ No newline at end of file