diff --git a/changelogs/unreleased/7169-kaovilai b/changelogs/unreleased/7169-kaovilai new file mode 100644 index 0000000000..f86c0547a4 --- /dev/null +++ b/changelogs/unreleased/7169-kaovilai @@ -0,0 +1 @@ +Add `--skip-immediately` flag to server, install, and schedule commands. \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_schedules.yaml b/config/crd/v1/bases/velero.io_schedules.yaml index 0cf3cbe52b..0994f74f64 100644 --- a/config/crd/v1/bases/velero.io_schedules.yaml +++ b/config/crd/v1/bases/velero.io_schedules.yaml @@ -61,6 +61,17 @@ spec: description: Schedule is a Cron expression defining when to run the Backup. type: string + skipImmediately: + default: false + description: 'SkipImmediately specifies whether to skip backup if + schedule is due immediately from `schedule.status.lastBackup` timestamp + when schedule is unpaused or if schedule is new. If true, backup + will be skipped immediately when schedule is unpaused if it is due + based on .Status.LastBackupTimestamp or schedule is new, and will + run at next schedule time. If false, backup will not be skipped + immediately when schedule is unpaused, but will run at next schedule + time. If empty, will follow server configuration (default: false).' + type: boolean template: description: Template is the definition of the Backup to be run on the provided schedule @@ -549,6 +560,11 @@ spec: format: date-time nullable: true type: string + lastSkipped: + description: LastSkipped is the last time a Schedule was skipped + format: date-time + nullable: true + type: string phase: description: Phase is the current phase of the Schedule enum: diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 65391cd542..89a6f3ec67 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -37,7 +37,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\xdc6\xf2\xbf\xebSt9\a\xfdSerb\xff\xb7\xb6\xb6\xe6f\xcb\xeb-\xed&\xb2ʒ}I\xe5\x80!\x9a$\"\x12@\x00p\xa4\xd9T\xbe\xfbV\x03\x04\x87\x0f̌F\xb5\xce\xf2\"\r\x1e\xdd?4\xfa\x8d,\xcb.\x98\x16_\xd1X\xa1\xe4\x1a\x98\x16\xf8\xe4P\xd2/\x9b?\xfc\xcd\xe6B\xad\xb6o.\x1e\x84\xe4k\xb8\xea\xacS\xedg\xb4\xaa3\x05~\xc0RHᄒ\x17-:ƙc\xeb\v\x00&\xa5r\x8c\x86-\xfd\x04(\x94tF5\r\x9a\xacB\x99?t\x1b\xdct\xa2\xe1h<\xf1\xc8z\xfbC\xfe\xe6m\xfe\xc3\x05\x80d-\xaeA+\xbeUM\xd7\xe2\x86\x15\x0f\x9d\xb6\xf9\x16\x1b4*\x17\xea\xc2j,\x88veT\xa7װ\x9f\b{{\xbe\x01\xf3\xad\xe2_=\x99\xf7\x9e\x8c\x9fi\x84u\xffJ\xcd\xfe(\xac\xf3+t\xd3\x19\xd6,A\xf8I+d\xd55\xcc,\xa6/\x00l\xa14\xae\xe1\x86`hV \xbf\x00\xe8\x8f\xe8ae\xc08\xf7Bcͭ\x11ҡ\xb9\"\nQX\x19p\xb4\x85\x11\xday\xa1\xdc*\x0e\x01 \x04\x84`\x1ds\x9d\x05\xdb\x1550\v7\xf8\xb8\xba\x96\xb7FU\x06m\x80\a\xf0\xabU\xf2\x96\xb9z\ryX\x9e\xeb\x9aY\xecg\x83x\xef\xfcD?\xe4v\x04\xda:#d\x95\x82q/Z\x84\xc7\x1a%\xb8ZX\b\xa7\x85Gf\t\x8eq\xfe\x94i\xc6~\x9e\xb6[\xc7Z=Ape\x90\xed\xb7\x06\b\x9c9L\x01\x18\xe4\t\xaa\x04W#I\xde+\x16\x13R\xc8\xca\x0f\x85\x9b\x00\xa7`\x83\x1e\"r\xe8t\x02\x99\xc6\"\u05ca\xe72\x12\x9d\xc0\xba\x99\x8d\x9e\x92\r\xad\xffo\xa3\x9a\x00\xbaU\xfc\x05P\xce\xe2\x1b\x16O\xb8~\x1d\x0f\x9dԏ\x1a\xfd\x9aȼӍb\x1c\r\xb1\xaf\x99\xe4\r\xd2\xcd2p\x86I[\xa29\x00#n\xbb\xdf\xe9)\x98/\x91\xdeh\xe6\x1ca\xf4\xb6s\xe7\x94a\x15\u008f\xaa\xf0\x0e\x8aT\xda\xe0D\xa7m\xad\xba\x86\xc3&r\x01\xb0N\x99\xa4\x82\x13Ⱛ\xa7\x1b\xc9\xce\xecl\xca\xf30\xfa\x11\xed\xe8O\xf3\x82lD(\x99\xb6\xa0w\x15\xa6\xad'Lo\xdf\x04wU\xd4زu\xbfRi\x94\xefn\xaf\xbf\xfe\xff\xddd\x18@\x1b\xa5\xd18\x11\xddg\xf8F\xc1a4\nSQ_\x12\xc1\xb0\n8E\x05\xb4A\a\xc3\x18\xf2\x1eC\xb8\x0ea\xc1\xa06hQ\xba\xb1H\xe2\xa7J`\x12\xd4\xe6W,\\\x0ewh\x88L\xbc\x98B\xc9-\x1a\a\x06\vUI\xf1\uf076%]#\xa6\rs\xd8{\xf1\xfd\xe7\x1d\xadd\rlY\xd3\xe1k`\x92C\xcbv`\x90\xb8@'G\xf4\xfc\x12\x9b\xc3O\xca \bY\xaa5\xd4\xcei\xbb^\xad*\xe1bP,T\xdbvR\xb8\xdd\xca\xc77\xb1\xe9\x9c2v\xc5q\x8b\xcdʊ*c\xa6\xa8\x85\xc3\xc2u\x06WL\x8b\xccC\x97>0\xe6-\xff\xce\xf4a\xd4^N\xb0.\x14#|>\x98\x1d\xb9\x01\ng ,\xb0~k8\xc5^\xd0\xd1\x1d}\xfe\xfb\xdd=D\xd6\xfe2\xe6\xd2\xf7r\xdfo\xb4\xfb+ \x81\tY\x92Y\xd3%\x96F\xb5\x9e&J\xae\x95\x90\xce\xff(\x1a\x81r.~\xdbmZ\xe1\xe8\xde\x7f\xeb\xd0:\xba\xab\x1c\xae|\xa6@\xee\xa9Ӥ\xb9<\x87k\tW\xac\xc5\xe6\x8aY\xfc\xe6\x17@\x92\xb6\x19\t\xf6yW0Nr拃\xd4F\x131E9p_\xb3\xbc\xe3NcA\xb7G\x02\xa4\x9d\xa2\x14\xbd\x87*\x95\x016_\x9eO\b\xa7\r\x97\xbe\xa4w\x9a/\x9a!{\x9f\xda\x13\xb1ɑO\x8d\x0e3\xac\\\x10\x05h\xe6^v\xd8cP++\x9c2;\"\x1c\x1cl\xbe\xa0p\xe0\x1a蓊\xe3\x89s\xdc(\x8e)ش\x15\\͂\xb6R~E\xfe\xa8\x93rɅ>%\xcf\x02\xa6\x15?\x81\xab\xe7\xc8\xc0`\x89\x06e\x81\xd1q\x1dK\x1e\x12\xc8\xc6a}\x89\xf1\xb0R\xc0\x11\xaf\x9eD\xfc\xee\xf6:z\xf2(\xc4\x1e\xbb[\xf2=!\x1f\xfaJ\x81\r\xf7\x81\xee4\xef\xcb\xeb20\xf3>\xcd)`\xa0\x05\x864p\b\x12 \xa4u\xc88\xa82I\x91j\x12 \xc37\xd8\xefx\x1d\xad\x032R\xa8\xca{b\x8a\x9a\xa5\xa0l\x86Ҙ\x10\x8b\xbd6.\x82y\xfcl\x17\xd4\xc7)(j&+\f\xe7E(;\x8a\x8e\xf9\xe5K\xecx\x99\x92\xc4/\x91\x9a\xcc\x1d\xc7\xff,\xb8?\xf3p>\x83~\xc6\xe1\xc6U\xc6\xd1\xc3=t\x1b4\x12\x1d\xfa\xf3qUX:Z\x81\xdaٕڢ\xd9\n|\\=*\xf3 d\x95\x91jfA\a\xecʗ\xa9\xab\xef\xfc\x9f\x17\x9f\xc5W\xb4\xcf=Ф\xd2\xfe\x96\xa7\">v\xf5\xa2C\xc5\x1c\xf6\xf9q\xec\xf2\xaeϬ\xe6{\xc9,\x1ekQԱ8\xe9}\xec\x01c\x12\x94\t\xf3\xe0\x9a\x99\xdc}sU&\x81v\x86\x10\xed\xb2\xbe\x97\x961\xc9\xe9\x7f+\xac\xa3\xf1\x17I\xb0\x13\xcf2\xdf/\xd7\x1f\xfe\x1c\x05\xefċl\xf5@\x02\x1e\xbe\xa7l\x0f+k\x99\xce\xc2j\xe6T+\x8a\xd9j\xcaJ\xaf9\t\xbe\x14hN\xa4q\x9f'\x8bc\xa2\x99\xc8o\x875g呎U\x89\xc4m\xdc:<\x96\xde\x1d\x95״q\xc3*\v\xcc 0h\x99\xa6{~\xc0]\x16\x12\x02\xcd\x04Es\n\xd8CW\x04\x98֍H\x06\xee>\xec\xf7)k/\t*\xcbYe\x0f\x9d=yk\xb1\vt\xa5d)\xaa\x13\xf7\xf0e\xb2x\xc8\tlL;JQuf_J\x8d\x9bS\xe7&͚\x19\xd64\xd8|\x14\r\xda\xc0\xf7\x19\xf6s\xbb\xdc5\x94%]\xbbACB/ir`p\xc0\xef\x04\xdc>\xbf\xd2hJe\xdaВ\xedl,\x17\x0e\x9fl/r!\x1dVC\a\xee\xac\v\xb9\xa7%ϻ\x0eZ\x1a\x8fy\xa21\xe8ꔣ\x9d\xb4\v\x97\xe7AٵK(\x19<(-Xbܠu\v\x83\xa7\x89W\xcbD\xf1\x88\xf5\x04\xe5>!\x83\xbe_\x9f\xa8={\xdb\b\x89\xfaP\x7f\xfa\x16m\xaa\xc0;\\\xdd\x1d\x84h\xf0\xb7\x8eʎ)\xc4,\xdd\t\x98\xad\xa1\xcax6\xa4\x15\xbf\x98\vr\xec\xf7f\x93\x936\xf2\x18\xe9\xb2=\xe2_\a\xceh\x90\x84W\x8f^\xa6!\x1c\xba\xf8\x16B\xb5\xe0K[$\x85\xa22k\xd2b=q\xbdW\xcb\x1d\xbe\x1bix\xaf\xee\xa2%w:z#\xe9y\xa4z\x1c0\"\x17v\xfa\x04\x84\xa8!\xf75\x10\x95h%\x13\rr\x88\x0fa\xf3=\t\xaac*\x1b,)Z\aӋ\xae\xa2\x877\xd4\x195\x82\xf5m\xbeK{\x84fg\x91{?\x9a\x10²\xf6 \x17\xc5\\hKgI\xa2\xb2k\x1a\xb6ip\r\xcet\xcb\xe9#\x96آ\xb5\xac:e\x8a?\x85U\xa1\xe1\xd2o\x01\xb6Q\x9d\x1b:.\x93xui{\x9d:\xaf\xe9\x93\xece̢\x00՚\xb6\xaf\xb9\x9a\xc6\xef\x19;\x82\xfd#\xa9G\xb5\xc1t\xce\xf6\x12\x9f\x00\xe0_\xffN!\xa45)\x03\x1b\xbc\xd7Q\v\x83#N\xf9\x06\x1f\x13\xa3\x8bW\xcb\xf1\xe4U4\x99\xc4\xdcGo\rg\x9d\xbfgtJ\x04\xfd2\xa8U\x13\x8dY9\u058c\x02\xf5f\xe7\xd0N\xddy\xaa\xbd\xe6\xcb\xf2\xbd\x18G\xfb\xe3\xfd\x05J}\xa7\xa1`ҷ\xc1ɺ\x9c\x02.\xacn\xd8.\x95\x82D\x84\x94z\x93q\x91\v\xd8\xebs4j\x8d!\xe597\xc3\xf1\x98>(y\xa0P\x8c\xf6,\xa4\xfb\xeb_^\x90b@\x10\xe7{\xe2\xf2m8\x1cIb\xacd\xda\xd6\xca]\x7f8\xa1\x05w\xc3\xc2h\r\xfb\x1c~\xc8!#\xb5^\x15RW5\xf8\x96\xb3Lu\xfa^~\n\xead\xf1\x89(Կԧb\xd0\x1dR\xea\xe90<\xe9\\\xcd\xdf\x1c_\x83\x15\xbeOM\xa5@\xa8\rB\x17\xc9Rp\xa2\xd4J\x19L\xb8LX\x86\x95I\x10\x99\xc2\xff3\xe3GRO\x16\x83\x1e9\x1f\xd1\xee\xdf:\xc6#\xddfx\xc7[\xc3\xef\x7f\\\xfc'\x00\x00\xff\xffo\x05\xb2\xa4Q#\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y_\x93۶\x11\x7fק\xd8q\x1e\xae\x991\xa9\xd8\xedt:z\xb3\xef\x9aε\xc9Yc\x9d\xfd\x92\xc9\x03D\xacHD$\x80\x02\xa0tj&߽\xb3\x00A\xf1\x9f\xa4\xd3M.\xe1\x8b}\xc0b\xf1\xc3\x0f\xfb\x0f\xab$IfL\x8b\xafh\xacPr\x01L\v|r(\xe9/\x9bn\xffaS\xa1\xe6\xbbw\xb3\xad\x90|\x01\xb7\xb5u\xaa\xfa\x8cV\xd5&\xc3;\xdc\b)\x9cPrV\xa1c\x9c9\xb6\x98\x010)\x95c4l\xe9O\x80LIgTY\xa2Ir\x94\xe9\xb6^\xe3\xba\x16%G\xe3\x95ǭwߥ\xefާ\xdf\xcd\x00$\xabp\x01Z\xf1\x9d*\xeb\n\rZ\xa7\f\xdat\x87%\x1a\x95\n5\xb3\x1a3R\x9e\x1bU\xeb\x05\x1c'\xc2\xe2f\xe3\x00z\xa9\xf8W\xaf\xe7s\xd0\xe3\xa7Ja\xdd\x7f&\xa7\x7f\x10\xd6y\x11]ֆ\x95\x138\xfc\xac\x152\xafKf\xc6\xf33\x00\x9b)\x8d\vx (\x9ae\xc8g\x00\xcd9=\xb4\x04\x18\xe7\x9e9V.\x8d\x90\x0e\xcd-\xa9\x88\x8c%\xc0\xd1fFh\xe7\x99i\xf5\x80ڀ+\x90\xb6\xf4\xac2!\x85\xcc\xfdP\x80\x00N\xc1\x1a\xa1A½2\x80_\xac\x92K\xe6\x8a\x05\xa4D\\\xaa\x15Oe\xd4\xd9\xc8\x04\xce\x1f\x06\xa3\xee@\xe7\xb0\xce\b\x99\x9fB\xf6;\x83\xea\xe1Y*\xfeL$\x8f\x05z\x99\x88\xa6֥b\x1c\rm^0\xc9K\x042Pp\x86I\xbbAs\x02E\\\xf6x\xd0}$_\xa2\xbe\xce\xcc5\xec\\CE\x90\xedm\xff\xb5;tiߥ\xe2\xcd\x02h\x8c\x1a\xacc\xae\xb6`\xeb\xac\x00f\xe1\x01\xf7\xf3{\xb94*7h\xed\x04\f/\x9e\xea\x82\xd9>\x8e\x95\x9fx]\x1c\x1be*\xe6\x16 \xa4\xfb\xfb\xdfNck\x16\xa5N9V~<8\xb4=\xa4\x8f\xc3ဖ\x9c-o\xae\xffO\x81\xbb&HwJ\xf6y\xfd8\x18\x9d\x02\xdbQ\x1a\xe3m\x9a\x19\xf4\xa1\xf6QTh\x1d\xabtO뇼\xaf\x8f3\x17\x06\xc2\xf4\xee]\beY\x81\x15[4\x92J\xa3\xfc\xb0\xbc\xff\xfa\xd7Uo\x18@\x1b\xa5\xd18\x11\xa3k\xf8:ɣ3\n}foHa\x90\x02NY\x03mp\x8a0\x86\xbc\xc1\x10\x9cEX0\xa8\rZ\x94!\x8f\xf4\x14\x03\t1\tj\xfd\vf.\x85\x15\x1aR\x03\xb6Pu\xe9#\xd0\x0e\x8d\x03\x83\x99ʥ\xf8_\xabے\xefѦ%s\u0604\xf8\xe3\xe7c\xb0d%\xecXY\xe3[`\x92C\xc5\x0e`\x90v\x81Zv\xf4y\x11\x9b\u008fd!Bn\xd4\x02\n\xe7\xb4]\xcc\xe7\xb9p1if\xaa\xaaj)\xdca\xee\xf3\x9fX\xd7N\x19;\xe7\xb8\xc3rnE\x9e0\x93\x15\xc2a\xe6j\x83s\xa6E\xe2\xa1K\x9f8ӊ\x7fc\x9a4kozXGN\x17>\x9f\xeb\xce\xdc\x00%;\x10\x16X\xb34\x9c\xe2Ht\fٟ\xff\xb9z\x84\xb8\xb5\xbf\x8c!\xfb\x9e\xf7\xe3B{\xbc\x02\"L\xc8\r\x05]\xbačQ\x95\u05c9\x92k%\xa4\xf3\x7fd\xa5@9\xa4\xdf\xd6\xebJ8\xba\xf7\xff\xd6h\x1d\xddU\n\xb7\xbe\x92\xa0xYk\xb2\\\x9e½\x84[Vay\xcb,\xbe\xfa\x05\x10\xd36!b\x9fw\x05\xdd\"h(\x1cX\xebL\xc4\n\xe6\xc4}\r\xab\x92\x95ƌ\xae\x8f\x18\xa4\xa5b#2\xef\x1b\x14~\x80\x8d\xe4Ӟ\xeaiץoͲm\xadWN\x19\x96\xe3\x0f*\xe8\x1c\n\r\xb0}\x9cZ\x13\xc1\xc9N\xce\v\xca\xc1\x06ɑR\x802.\xde\x17h\xb0\xbbƠVV8e\x0e\xa48d\xcbt\xa4\xe1\xc4E\xf8#+~\xe1\x18\x14\xee\xbdC\x18ܠA\x99a\x8c\x10\xe7*\x99\x89St\x12\xfa\x18\xe2i\xea\xe1L\xf4\x9c\x04\xfcay\x1f#fd\xb8\x81\xee\xc6\xfb^\xa0\x87\xbe\x8d\xc0\x92\xfb\x84ry\xef\x9b\xfbM\xd8\xcc\xc7\x0e\xa7\x80\x81\x16\x18*\xd26\x18\x83\x90\xd6!\xe3\xa06\x93\x1a\xe9m\x00\xe4`\x06\x9b\x15oC\xa4hB\xd21\x84\x13\xf5\xc0(F\t\x0e\xff^}z\x98\xffk\x8a\xf9\xf6\x14\xc0\xb2\f\xad\xf5\xf9\x1a+\x94\xeem\x9b\xb39Za\x90S\xe1\x82iŤؠui\xb3\a\x1a\xfb\xd3\xfb\x9f\xa7\xd9\x03\xf8^\x19\xc0'V\xe9\x12߂\b\x8c\xb7\xe1/ڌ\xb0\x81\x8eV#\xec\x85+\xc40i\xb5\f\x90u5\xc7\xde\xfb\xe3:\xb6EP\xcdqk\x84Rlq\x01o|%x\x84\xf9+9\xd6ooNh\xfdKp\xa07$\xf4&\x80k\xf3]\xd7#\x8f ]\xc1\x1c8#\xf2\x1c\x8f\x85\xe8\xf0\xf3\xc1\x9bBⷠ\f1 UG\x85WL\xb7\x17\xe2\x11\xf2\x11\xe8\x9f\xde\xff|\x12q\x9f/\x10\x92\xe3\x13\xbc\a!\x037Z\xf1oSx\xf4\xd6q\x90\x8e=\xd1NY\xa1,\x9ebV\xc9\xf2\x10\xaa\xfd\x1d\x82U\x15\xc2\x1e\xcb2\t\xf5\x06\x87=;\x10\v\xf1\xe2\xc8\xde\x18hf\xdcYk\x8dU\xc6㧻O\x8b\x80\x8c\f*\xf7\xf1\x8e\xb2\xd3FP\xd5@\xe5B\xc8y\xde\x1aGI3~\xb6\x0e\xe6\xe3\x14d\x05\x939\x86\xf3\"lj\xcaB\xe9\xcdK\xfcx\x9c\xfa\xe37Q\x02\f\x03ǟ\x96D\x9fy8_\xa9>\xe3pݷ\xd6\xd9\xc3m\xeb5\x1a\x89\x0e\xfd\xf9\xb8\xca,\x1d-C\xed\xec\\\xed\xd0\xec\x04\xee\xe7{e\xb6B\xe6\t\x99f\x12l\xc0\xce\xfd\x93y\xfe\x8d\xff\xe7\xc5g\xf1\xaf\xeb\xe7\x1e\xa8\xf7\xe8\x7f\xcdS\xd1>v\xfe\xa2C\xc5Z\xf1\xf9y\xecf\xd5\x140õ\xe4\x16\xfbBdE|\x0441\xf6\x843\t\xaa8y\b\xcdL\x1e^ݔ\x89\xd0\xda\x10\xa2C\xd2\xf4\xb4\x12&9\xfd\xdf\n\xebh\xfcE\f\xd6\xe2Y\xee\xfb\xe5\xfe\xee\x8f1\xf0Z\xbc\xc8WO\x14\xba\xe1{J\x8e\xb0\x92\x8a\xe9$H3\xa7*\x91\r\xa4\xa9\xf6\xbb\xe7D\xfcF\xa0\xb9P\xc5}\xee\t\xc7*t\xa2\x8ale\xae*#\xadd\xda\x16\xca\xdd\xdf]\xc0\xb1j\x05#\x86\xe3u5\xc5c\xd45h\x02]\x87\xc7\xfb\xcb\xc3\xe9@\xd2\a\u0557\x8eȔ\x11\xb9O[\xad\xef\xfbW\x84d\x15\xeb6\xff\xba_Ŵ\x162\xbf\nk\xb7\x97v\x01藎hDy\xa1\x9b\xe7\x8a)\x9c\xbd\x1e\xdf\x18-ʺ\x1aCI`\xab\xb4`\x13\xe3tG#\xfb\xa4\x897\xe3\xba\xe6\f\x13\xc1\x00.pд\x9e&\xdeQ\x8d\xfd\x84\xbaҏ\xd0\xdb\xc5[\xd1t@\xbe֮\xe8\xd9MEr\x1fa2\xfd:\x1c\xc8h\xc5gCҺ.9\x98<:\xd4p\xa2o\xab\x83\xd9^K\xb4{\x9a\xf1\xc3\xda\xf7ۮyZ\x87\x1e_\xc3{\x88\xf0.v\xfe\xe8y\xf3\xe2\xc7u\xa6\xe8\xe9\xd0k\xcf]\xb0\x81\xdb\xf1\n\xdf\xc92\xbc\xf1\tQ\xa1\x7f\xb1\x86\xf6\xe4\x9eٸ\xc9\xd4}CG_X\xea\xb3*\xa9C\xee\v{zwl\x98(\x91C\xfb+\x8bo\xa5[\xdfҹ\x99\xaac\xa3\xa2\xda\"\xf7qc\x02\xf4x]\xec\x92r\xe60!\x15#\tY\x97%[\x97\xb8\x00g\xea\xf1\xf4\x19\xf7\xaa\xd0Z\x96_\xf2\xaf\x1f\x83Tx\xf37K\x80\xadU\xed\xdaG\x7f\xe3h\r\x157\xb6\xb1\x82\xeb\x1a\x0f\x05\xb3\x97\xa0,If\xca\xe2Z\x97?orp&\x94=\xe0~btԵ\xeeN\xdeF\x13\x9a\x98\xfb\xde[\xc7U\x044\x1b]\xe2\xa0\x11\x83B\x95Ѻ\x95\xa3\xa4TWk4D\x84o\x95GFb\xe0\x98\xea\xa2\xf8\xd7בɣ\x86\x18\v\x83\xaa\xe6=\x991雊d\xbfN\x01\x17V\x97\xec0\xa17\x9e\xc4\x17Xd\xbe\xe4GG\x8b\x89^H\xee\xef\xe7\xae\xed\xfe\xb4?\x05L\x97\x7fS?,L\xddB\xf7W\x82\xc1|\xfb\x1b\xc8\xeb\xecp\xa6䳎\x19\xf7ܰ\xb7\xea\t_\x8ax^\xf5t\xbc놮q\xa0\xeao\xf3GƨI\xa2F\x83\x1e9\xef\xe8n:\xa7ݑz\xdd\xfe.\xb0\x80_\x7f\x9b\xfd?\x00\x00\xff\xffg\b\x17r\xc1\x1f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xdc=Msܸrw\xff\n\x94spR\xa5\x19?'\x97\x94n\x8a\xd7\xceN\xde[[%\xb9\xbcg\f\xd93\xc4\x13\tp\x01p\xe4I*\xff=\x85\xc6\a\xbf@\x12\x1ci\xf6\xed\x067q\x80\x06\xd0\xdd\xe8o@\x9b\xcd\xe6\r\xad\xd9w\x90\x8a\t~Kh\xcd\xe0\x87\x06n\xfeRۧ\x7fW[&ޟ>\xbcyb<\xbf%\x1f\x1b\xa5E\xf5\x00J42\x83\x9f\xe0\xc08\xd3L\xf07\x15h\x9aSMo\xdf\x10B9\x17\x9a\x9a\xcf\xca\xfcIH&\xb8\x96\xa2,An\x8e\xc0\xb7O\xcd\x1e\xf6\r+s\x90\b\xdcO}\xfa\xcb\xf6ÿn\xff\xf2\x86\x10N+\xb8%\x12\x94\x16\x12\xd4\xf6\x04%H\xb1e⍪!30\x8fR4\xf5-i\x7f\xb0c\xdc|v\xad\x0fv8~)\x99\xd2\x7f\xed~\xfd\x1bS\x1a\x7f\xa9\xcbFҲ\x9d\f?*ƏMIe\xf8\xfc\x86\x10\x95\x89\x1an\xc9\x173MM3\xc8\xdf\x10▎\xd3nܪO\x1f,\x88\xac\x80\x8a\xda\xf5\x10\"j\xe0w\xf7\xbb\xef\xff\xf6\xd8\xfbLH\x0e*\x93\xacֈ\x00\xb76\xc2\x14\xa1\xe4;\xee\xcd,\x00qMtA5\x91PKP\xc0\xb5\"\xba\x00B\xeb\xbad\x19\xa2:@$D\x1c\xc2(E\x0eRT-\xb4=͞\x9a\x9ahA(\xd1T\x1eA\x93\xbf6{\x90\x1c4(\x92\x95\x8d\xd2 \xb7\x01V-E\rR3\x8fX\xdb:\xec\xd2\xf9:\xd8\xcb;\xb3]ۋ\xe4\x86O\xc0.١\fr\x87!\xb3Z]0\xd5nm\xb8\x1d\xb7%ʉ\xd8\xff\x1d2\xbd%\x8f \r\x18\xa2\nє\xb9a\xaf\x13H\x83\x9cL\x1c9\xfb\xef\x00[\x99\x8d\x9aIK\xaa\xc1ѻm\x8ck\x90\x9c\x96\xe4D\xcb\x06n\b\xe59\xa9\xe8\x99H0\xb3\x90\x86w\xe0a\x17\xb5%\xbf y\xf8AܒB\xebZݾ\x7f\x7fd\xda\x1f\x93LTUÙ>\xbfG\x8eg\xfbF\v\xa9\xde\xe7p\x82\xf2\xbdb\xc7\r\x95Y\xc14d\xba\x91\xf0\x9e\xd6l\x83K\xe7xT\xb6U\xfeO\x81l\xefzk\xd5g\xc3yJKƏ\x9d\x1f\x90\xcdg(`\x18\xde\xf2\x92\x1djw\xd1\"\xda|2\xd8y\xf8\xf4\xf8\xad\xcbgL\r\xb1\x8fx\xef0_K\x02\x830\xc6\x0f -\x11\x91\xdb\fL\xe0y-\x18\xd7\xf8GV2\xe0C\xf4\xabf_1m\xe8\xfe[\x03\xca0\xb4ؒ\x8f(;\xc8\x1eHS\xe7TC\xbe%;N>\xd2\nʏT\xc1\xd5\t`0\xad6\x06\xb1i$芽ag\x8b\xb5\xce\x0f^xM\xd0˝\xfe\xc7\x1a\xb2މ1\xc3\xd8\xc1\x1dsr\x10\xb2'\x1c̐m\x0fh\xfcКfO\xbf\x91`\xc3_\x06K\xf9\x8f\xd0\xd1\xf0\x8fYD\xc3\xd9o\r\xa0\x88\xb3'\x16F\"e\x04\x92\xf8\xf5![lG\xbfO\xe0\xd44\xf8\x91\x95M\x0ey\x90\xb6\xa3\xbd\fV\xfci4\x00\xb5\x0ee\xdc\xf0\xbf\x11\xfffټ\xfdՈ\xd3Ȋ\xa9\x04b8\x90q\v\x8f0\x8e\x9b\x8db\xda4\xa6\xa1\x8a,nvw\x84\xf0\xa6,龄[\xa2e\x03\x13\x98\xa1R\xd2\xf3\x04b\xbc\nN\xc5K\xe8\xef\x04B\xc92\xe8*\n\x8b\x1a\xabd\xa8\x1c\xaf\x88\xfc\xc1\xb1\u0094\x11g~\x97\xf7\xa2d\xd9y\x115\xb1A\xfe\xb8\xb9\xc3\xe79x\x0f\x05=1!#[2'\xd2t\xed(\xd2V\x98\n#\xcb\x1c\x90\xfc\xb2\rG\x91U\b\xf1\xb4D\xfb\x9fM\x9fVj\x93\f\x8d\xb7\xb0\x15Gm\xa7D\xf7@\xe0\ad\x8d\x8e,\x93\x90\xbcA\x05\"$\xa9\x85\xd2\xd3t\x9f\x96=Ċ\x83)\xa6%sL3ڙ\x13\x95\x9erf\xa3=\xb1)8\x98\xb5V\x86rm_)\x1a\xdbw\xa8\xdf:\x18\x8fc\x84쩂\x9c\b\xc7\xf5M\t\xca͕#\xf9[\xb9r3\t:l\xdeZ\x1a%\xddCI\x14\x94\x90i!ǘL\xc1\xa7m)\xb2r\x02\x8f\x11\xa9\xd9g\xffvc3 \x89a\xf3\xe7\x82e\x855\x02\fo\"\x1c\x92\vP(8\x8c\xa1z\x9e\xda$Y\xa2\xbd\x9bdNt\xb4m\xe1L\r\xe1\xc5\xc4I\xdb\x12\xc4m\xdb\x16\x04\xefH\xb0\xb8\xefQ\xcdٶ\xff\x9f\x88\xf5\x9a\xe4\x02\xa6ݍ\x86\xbe.ӢSe\x8c\xfd݁@U\xeb\xf3\ra\xda\x7f]\x82H˲3\xff\x9f\x980\xeb9~7\x1c\xf9\xaa\x1c?K\x95%\x88\x86*a\xfa?!QPY<:]\x91L\x90\xbfuG\xdd\x10v\b\x04\xc9oȁ\x95\x1a\xe4\x802/:/\xaf\x81\x8c\x14}gZEuV|\xfaa,\x1b\xd5ƙ\x12\xf12\x1clMb\xef#\xf4\x15\xf3\x02\\\x82\xee+\x93PY\xb7\xf8\x1bb\xb3\xfd\x82\xfe\xc4ݗ\x9f \x9fC\x0fI\xe3\xbc\xd1F\xee\x06\x8b\xedN\xed\xec\xfc\xd4m8\xd3'\xf8L6\xe0qC(y\x82\xb3\xb5X('\x868T\xa3\xbd\x1b\xf5\x9e\xc6\xc8\xc1\xc8\v2\xd9\x13\x9c\x11\x8c\v\xa5,\x8eNe\x05۞ b\xee\xc7Z\x0f\x81fM\xce\xc1\xb5\x984\x1f\x10\x11\xe8x\xa7#\x8f`X\xccˢ\xe5͑tA\xe2\x9b\xc7\xfd\x05\xdb\fd\xeb\x84\x0f\x91\xb0\xef\x94%\x919\x05\x05\xab\x137\x8a\xd1C\x05xZ|`\xec;-Y\x1e&\xb2|\xbf\xe3\xd3\xd6p\xbf}\x11z\xc7o\xacG\xa6\x90K~\x12\xa0\xbe\b\x8d_\xae\x82N\xbb\xf0\v\x90i\a\xe2\xf1\xe2Vl\x1b\xc1\xf9\xed\xcdH\x0e\xbc\xdd\xf1\xb7V\xc1\xaf\x167\xc1Z\x10\xbc<\x93\xb78\xf6\xedK\x8c\xa0DNL\xec\xf6c\xf3\x14Br\x9b\x8a\xd6\x1bǽZT,\x9b\x1cǣ\xe1\xf1\xb6\xf5ة\x1b\"oc\xe3\xce<\x9e\xdbm\x12\xff\xd6B\xe9\x9fま\x89\xf5\xdc\xfb\x11}\x9b6\x12/[\xb4\xf5]\xec+\bcc\x01\x1e4H\x17\xfc\xb3\x02\xda{\x0e/\xf4\xa9\x96\x82{!\xb0GC@\xd6 x\x81\x9bl\xaa$e\x89k\xacM\x83\x97\x95v\xfa\xa7\x1f\x9dؤ9\xd9\xe6\xef\xeeF^\xdb\x1a\xceDU\xd1ar0i\xa9\x1f\xedH\xcf\xd3\x0e\x90\xa5\xbe<6x\x9e\xd3\xcdD\xcfC\x98\x16|f\xba`\x9cP/6@:\x86\xa2\xa4\x16\xcb\x12̶\x82*\xb2\a\xe0!\xa6\xfeG\xd0\xf3\x15\xe3;\x9c\x80|xu\xbb\x80\xb4躈\x9c\x1eՁ\xa0\xe1\x03j\xaaT\x93J\xe4\xe4\xb9\x00\t=\xae\x18\aʍ\xa5\x99\b\x92\vݍG\x18\xb8\xb5\xc8\xdf)r`R\xe9\xeeBS\x19\xaeQ\xa9찒\xc2fw\xdfX\x05\xa2\xd1\x17\xd0\xe0S;\xba\x97\u05ed\xe8\x0fV5\x15\xa1\x95h\x12\x8c\x02ی~aUH\xbe:\n\v\xf9\x004\xbf$\x00\xf3kg8\x01\xae\x1a\x89\x89{+^\x9eY\x99\xb6fC9R҆g\x05\xa0\x9c\xe2=\xf1A,xƕ\x06\x9a\xca\v\xc6jj8g\xfc\x98F\xbb\xe4\x10g\xdb,\xaa\xf7B\x94@\x87\x05O\xb1fp}\xb9\x18\xfa\xb5\x1d\xfd\xbb\x88\xa1@\x81tsa\x0f\x8eTN\x16Q\xad\xa1\xaa\xedy\x13D6\xbc\xab}\xae \x85\xd6\xf8\xe0n\x15\xaf\xe9\\3\xce\x12\b;ȹ0ݵ,\r\x88\xabZ\x96f\x82`T\\\x12>\xdb\xf5\x00\x98\xd3\xe9\x9d\x14\\{\xe0\x9a\x15V\xe6\x1e\b\xcds\xc8m`Ҙ*\xceg\xb1\xe5e\x13\xa5\n\xd1ݭ7\x13\x93(\xeb[\xcf#\xc5P\xac<\xc1\xa6\xe1O\\<\xf3\rz\xf2j\xb5\x00I\x0f\r\xbe\xea\xf4\xfabI\xf4{J\xa1>\xbf\xa6\xf3\x947\x9e\xae e\x92\xf9fU4d\x8e\v\x96\xe4\x9a-]\x9e\xf8qq\x15s\xf3\xcf\fv\x89揶\xe68\xb5\x9em\x17\x1f\xd51\xfe\x9e\v\xd0\x05H_̼\xc1\xba혜n\xf3ѭ\x1f\x13\n\xdc\f\xffxS\xd8\x16^\x0eJ\xde⎎\xb1\x02n\fcӦԶ\xfcX6\x11&J*\xfc\x8a[\x06)\x95\x13K\xf5\x12\xfd\x1a\xc0P\xaf\xe0\x8b\x00\x85\x9f$\xb2CKK[\xe9\xdbM\xc6\xf7\v\x1f0\xe4\xe7W\xfa\x0f/\x0fL\xa8iX\xa8d\x98/\x9a\x9c\xc3טm\xba\x18ky\xd0\xf5sմ\x7f,\xf4i\xa8\xbe\xd6\xee\x1cL\x1a\xa0}\fF\x86\f\xcaAPr\x1b\x97\x1d\v\v(\x8b\t\x17s\n]8\xd0@\xbc\xcb\xf0$\n\x0fXa\xa8ٝ6W\xdd\xce\x14\xf9@\n\xd1DJ\xeaf\xb0\xb3P`1]V\xe1\x92\b\xa0\xe9\xe9ö\xff\x8b\x16\xae\xc8\x02#_\x91ݡ\xa3\xd2FS\x19\xcfى\xe5\r-{\x87\xac\xc3\x16-\xf7\x10!\tge,\xbfj\xd8ʏ\xef\xb1\x11\xf9Z\xdb<\xcbjq4o\"\xa6\xd5b\\\\\x81ѯ\xb0\x98PRkS\x0e饦\xe95\x16\xf3E\x11k*+\x86u\x13\x93@\x97\xeb)R\xac\xfb\x85ډ\v*&\x12\xab\xe5^\x9c I\xa9\x89\xb8\xa8\x12b\xb1\xa0,\xb1\xfe\xa1_\xd90\x0frE\xd5C\x12r\x96+\x1cV\xd75\xb8:\x82\xd9}$W3D\xea\x14f\x01O\xd60\xccU'̣\xff.\xf0V\xd0\xfe\x8c\x90\xbe>\x84Ӹ\x1d8\x0e4v\x86\x9e\xa1,\tU\xe3\xedg\xf6bl&6x\xd7\xcdP\xd2\xf3\x83\xbb@{\x83'6\xe6\xb1s\x7fe\xb32`\xf0r\xad\x8aDD&uѼ=lMw\xfc\xf6[\x03\xf2L\xc4\ts\xfa\xce@Z(\xbb\xb7rE\x19\xff\xcdK:'.\xedu쁟\xd0\xca\x17rǭƎ\x82\x1d\xac\x11\xe1\x18\x11\xd7\xfaFF\x9a\x1b\xb7g\xa2k\x14*\x17at\x9c\x1ff\x15Sj\xcd\xfau=\xa5\xf5\xbeҢ\x95r\x15\x7f\xe9r\x8fi\x06dj\rzZNd\xb1\xe6\xfcZ\x9eӒ\xef\x94l4\xa6Ք_\xa3\x96|E\r\xf9\n\x1fj\x9d\x17\x95\x8c\xa6\x94Z\xf1\xab\xf8RW\xf4\xa6\xae\xe1O]\xe6Q-\x80\x1cԀ\xa7Tw'\xa5\xf1\x92s6)Y\xb6\xe5\xcc\xf1|\xd5vB\xb5vB6hi\xa5\tU\xd9몱\x13px%_\xebJ\xde\xd65\xfc\xad\xebz\\\x8b>\xd7\"\xe7,\xfc\xbc\xae\x8a\xfa\xe2$\x83OG\x7f\x119\xdc\v\xa9\x97\x1c\x84\xfba\xffH\n\xb0\xe34\x892'\xdcw\x8de\x1a\x8c\xed\xef\xec\xfe\xcb6\x15\xcf\xd6y\xf3\xf7\x17\x91\x9b\xb5-\xe5\x16\x1e\x06\xddGWh\x0f \x81ۇ%\xfe\xeb\xf1\xeb\x97\x00?f\x8f:\xa3w\xf0\xa6\x8150r\x87\x1c\x97}r\x057\x16[\xa8\xc3_9I@k\xf6\x9f\xf8f\xd7r@\xe6\xee~\x87]\xbd\xb5\x84o}\x85\x84~Ƚ\xed\xc1h\x8f\x80\x91I\xee\xdf\x1dz\x10#e\xa7\xe1O\x82/&y\xedŦj\xb2l\x11\x929u\xf7;\xbb\xba-\xf9lL7~&\xc22^\xc1d\xbe\xa9\xa9\xd4g\xe4\x0eu\x13\xd60\x1d\x94\xf1:d.t2)j\xc7oAEq럄\xc2\x04ܹ\xeeg3\x87\x18\xbdd\x1dӷ'\x16\xefM\xbc\xe2:\xa6\xd5\xf1\x061\x15\xf9\x1c\xad\x80x\xb5\x90\x94\x13C\xf7ߗ\xc4\xdaC\xe88/ό'\xeb\xc3:\x11\xfc\x98\xf1(\xd2\x14\xa7\xb5*\"Q\xa1\x97\xc94|\xa8JS\xdd$\xee\xc7\xf6\xedm\x89eEG\x00=\x83\x17Q\xb2\xf3l\xdf`M\xe6\xacZ@\xa8\x86ф欼\xe98\xe6\xbfO\xca3\xf1Y\x90\x8b\x1f\x04\xb1\xe8\x99\x10\x15\x18i2b\xcc\xf3B\x8b\x97\v\x92\x9d\x8b&\\Ba\xeb\xbcݙ\xf8\xa2\xc4\xc5oI,#+\x82\xa8\xa9g$R\x9e\x8a\xf8\x87\xe2sF$\xa9\xac\x80\xbc)!ၷ\xc7N\xd7\xe5'\xde<\xe0ؙ\x14\xfdG\xde\f^;\xea\xd5\x18\xbc\xfd\xc7\xe4\x1c\xd2\x1d\xe4\x89\x12\xef.Hk\xd9\xdbW\xa72cǫ&\xcb@\xa9CS\xfaj\x99L\x02Ր\xfb\xee\xd1\xca|\xbf\x87\x15e!q-\xb2\xe9<\xa3\xf7&\x812*\"&gDdFk\x8dU\xf0he4R\xe2\x96\xedo\xe20z\xfa\xaf\avZh\xb9rFW\x8c\xa34\xad\"\x96\xd8\xf0\"\xd8p\x04>\xb0)\xf3N\xf9N\xf7\x85\xb4P\x95\x13\vkS\x15**\xf3m\a\xb6\x05\x83Ə\x01\r9\x81\x13p\"8\xde5\x81|\xae|\xf7\x1b\x86\xcd\xe4\t\xe4;\x15\xe0`A\x91\xb1\x15\x1f5\x95:,}\xcc\x11\a!+\xaaoIN5l\xcc\xe8\xcb4d\xfc\xf5B)\x973\x1cxiŹ\xc1x\xd3\x04\xc9[\x96\xee\xaaI\x05Jѣ7ߟA\x029\x027(\x9e{\xa0\xad\xbd\xad\xe3Np\xa8;3آ\x99n\xa8\x9b\xc0jʐ\xfb\x89\xe5\r쫟\xe8\x11\x1c'\xcf\r\xe3\x1a\x8e\xa3\xac\x8b\xbb)\xf4\x00T\r_\x89\x1d!\xe2s\xb7\xaf\x8b\x99Y\x1c\xd8'I\xa8-\x12\xc3GE5\v^ʄ423\xaf*\xfd*\x84xJ2e~\x0e\x1d[\x97\x9dq\xcbGx?j/\x1a\xdd5R\xc4T\x80\x0f\x1f\xef{e\x93\x04a\xde\xd9\v\x13Sq\xa8\xf1\xeb\x80a@p\n\x84\xa6%\xe1M\xb5\a\x89\f\x19:\x143\x97\x86\x1f\xfd\x13\xa5ey\xbe\x19B\x1e\xbcY\xdc\u009e\x83\x88\xa4w2\xa0s\x91\xd4\aP\x06@l\xf7\x99\xf7\v\t>8\xeb\xf5\xc6ԋis\x1c\x1dp\xfc\x19\x17\x95\x8a`\xdb{\n\xbbv\xf1\xd6\x16\x06\x1e7\xe5\x89O`\xfacq\xc1\xd2g\x8c\x86\xba\xa0j\xc9Z\xb87}\xc2\rҎN\n\x86\xc2\xc3đ\x8c\xdfhې/\xf0\x1c\xf9j\x91\x85i\x80\xb8&ِ\x1d\xbf\x97\xe2(A\x8d\x19g\x83W\x9c\x18?~\x16\xf2\xbel\x8e\x8c\x87\n\xd4u\x9d\xef\xa9\xd4̰\xb2]Od\xecG\xaf\xcb\"\xbf-\x8f\x9e\xf8aFF\xd5nϋ11\xdbmI>9\x01\xfaN\xb5\xfa\"\x16?rж\xe4\x8b\xd0\xe0SU\xac\x0f\x94)\xb2\a\xa57p8\b\xa9m\bs\xb3!\xec\xe0\xec\x94X\xa8\x8d\xb2\x12Mm\xfbij\xb1\xbfCqU0\xfc\\8E\xa2R@\x1b\xbd\xa2g\x1b\x95\xa1Y\xd6\x185\xf8^i\x1a\xb3\xe7^$FѶwܜr\xc8w\xdd\xfe!\xa2\x11\x0e8\x82\xb3\xa8ë\xa5V\x03G\xd3\xf4\x04\xaf1vn\xb6\x13e\xb4\xd9%ǝXA\xb3\x9b\xf6S\xfa\xf7qB\xe7)9\xe5\xb6\xd1{\xccv\xba\x14\x97)?\xd4\xd0,+(?\x1a\xf6\x91\xa29\x16\x9e\x05\xa7\f\x95\t\xa0y\x83!\xcf\x1aO\xaa\xf2)_\xddH\xde\xc9X\xb8$p\xde.w\x0e\xe8\xc5\x12S\xb6\x05\xe9\xad̘Ѽ1c\x7fb\xf0\x04\xfec\xf1\xd40\x84\xaa3\xcf\xe6\xab\xe4m\xec\x94\xcd\\\x86\x9bCFt\xbfA\x02^\xb2\xdf08}\xbf]\xe5ݺ\x12k6?\xedf\xbe\x02:\xa6\x8c\x82e\\\xcc\x1b\b\xb8\xbf\xc8\xcaW\x91\xbbk`xS\"\x16\xd42\xc6\xc5:\\\xa8\x9e\x93\xb5\x14s\xe8u~\x993\x89\x13\x1bW\xf2\x8f\xeb\x04\x9e\x82\x19\xf3)\xc5\x1d\xfc>\xe8>\xb8rd\x1c\xc3\x16\xa2s\xe1\"\xc8\xf9gv\xf0\xff\x15d_¿\x8cz\xfc\xceW\x87\x9e\xa9\xe4\x8c\x1f\x976\xff\xab\xeb\x16\xf1\x86\x1d\x84\x88?\x1c\xd9D\xf0\x90W\xf9\xc3~\x91\x13\x0f\xdf\a\x1f\xf9\x05\x1eqT\x9d\x8c>\"#\xe7\x1d$\xbb\x99ܗ\xff\v\x00\x00\xff\xff\x0e;\xfc{\xabg\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec=Mw$)rw\xfd\n\x9e|\x18\xdbOU=m_\xfctk\xab{\xecz;ӭ\xd7\xd2\xf6\xc9\x17*3J\xc5(\x13r\x81,uy\xdf\xfew\xbf\b \xbf*\xc9$K\x92gfW\\\xba\x95\x05A\x10\x11\xc4\a\x04\xb0Z\xad.x%\xbe\x816B\xc9k\xc6+\x01\xdf-H\xfcˬ\x1f\xffì\x85zwx\x7f\xf1(d~\xcdnjcU\xf9\x15\x8c\xaau\x06\x1fa'\xa4\xb0Bɋ\x12,Ϲ\xe5\xd7\x17\x8cq)\x95\xe5\xf8\xd9\xe0\x9f\x8ceJZ\xad\x8a\x02\xf4\xea\x01\xe4\xfa\xb1\xde¶\x16E\x0e\x9a\x80\x87\xae\x0f?\xae\xdf\xff\xdb\xfa\xc7\v\xc6$/ᚙl\x0fy]\x80Y\x1f\xa0\x00\xad\xd6B]\x98\n2\x04\xfa\xa0U]]\xb3\xf6\a\xd7\xc8w落\xf3\xed\xe9S!\x8c\xfdS\xef\xf3\xcf\xc2X\xfa\xa9*j͋N\x7f\xf4\xd5\b\xf9P\x17\\\xb7\xdf/\x183\x99\xaa\xe0\x9a}Ʈ*\x9eA~\xc1\x98ǟ\xba^1\x9e\xe7D\x11^\xdcj!-\xe8\x1bU\xd4e\xa0Ċ\xe5`2-*K#\xbe\xb3\xdcֆ\xa9\x1d\xb3{\xe8\xf6\x83\xe5W\xa3\xe4-\xb7\xfbk\xb66To]\xed\xb9\t\xbf:\x129\x00\xfe\x93=\"n\xc6j!\x1f\xc6z\xfb\xc0n\xb4\x92\f\xbeW\x1a\f\xa2\xccrb\xa0|`O{\x90\xcc*\xa6kI\xa8\xfc'\xcf\x1e\xebj\x04\x91\n\xb2\xf5\x00O\x8fI\xff\xe3\x1c.\xf7{`\x057\x96YQ\x02\xe3\xbeC\xf6\xc4\r\xe1\xb0S\x9aٽ0\xf34A =l\x1d:?\x0f?;\x84rn\xc1\xa3\xd3\x01\x15\x84w\x9di \xb9\xbd\x17%\x18\xcb\xcb>\xcc\x0f\x0f\x90\x00\x8cHT\xf1ڐp\xb4\xado\xbb\x9f\x1c\x80\xadR\x05py\xd1V:\xbcw\xb2\x97\xed\xa1\xe4\u05fe\xb2\xaa@~\xb8\xdd|\xfb\xf7\xbb\xdeg6\x90%O)&\f\xe3\xec\x1bM\f\xa6\xfdLev\xcf-Ӏ\x9c\ai\xb1F\xa5a\x15\xa8\x9b7 \x19S\x9aU\xa0\x85\xcaE\x16\xb8B\x8d\xcd^\xd5Eζ\x80\fZ7\r*\xad*\xd0V\x84\xa9\xe7JG\xa3t\xbe\x0e0\xfe\x01\a\xe5j9I\x04C\xc2\xe7'\x14\xe4\x9e\x0en~\b\xd3\xe2OL\xea\x01fX\x89K\xa6\xb6\xbfBf\xd7\xec\x0e4\x82\tXgJ\x1e@#\x052\xf5 \xc5\xff6\xb0\rJ\xbd%a\xb4\xe0\xf5A[h\x02K^\xb0\x03/j\xb8b\\\xe6\xac\xe4G\xa6\x01{a\xb5\xec\xc0\xa3*f\xcd~Q\x1a\x98\x90;u\xcd\xf6\xd6V\xe6\xfaݻ\aa\x83&\xcdTY\xd6R\xd8\xe3;R\x8ab[[\xa5ͻ\x1c\x0eP\xbc3\xe2a\xc5u\xb6\x17\x162[kx\xc7+\xb1\"\xd4%i\xd3u\x99\xffS\xe0\xa8\xf9\xa1\x87\xeb\xc9|s\x85\x14\xe1\x04\aP#:\x81qM\xdd(ZB\xe3'\xa4\xce\xd7Ow\xf7]a\x12fH}\xa2{G\xc2Z\x16 \xc1\x84܁\x9f\xd1;\xadJ\x82\t2\xaf\x94\x90\x96\xfe\xc8\n\x01rH~SoKa\x91\xef\x7f\xa9\xc1X\xe4՚ݐyA9\xac+\x9c\x81\xf9\x9am$\xbb\xe1%\x147\xdc\xc0\xab3\x00)mVH\xd84\x16t-㰲\xa3Z\xe7\x87`\xde\"\xfc\ns\xfc\xae\x82\xac7e\xb0\x9d؉\x8c&\x06i\xcfF\x05\f4\xa8+㳖~!55\xfc:\xc0\xc3\xe9\xb2\xd0+\x18\xb4\x1fvO\x1cn\xcd\x18ʕ\x83\x86:E\xaa!wǴ`\x87\x12\x1e\xca\f&}\xad\x97j\xdfN`2\xaf\xea\xd6\x11\x1cO\xb8J?AY\xa1ژA\xf1\xdeWC\x14\x91>y\xe35\x05\xc3\x1fԬ\xf2ڕ\x9d(7\xean\x0fȷ\x83Ƚ\xf6:\xe1*\x9b\xe4,\x96̈;\xc9+\xb3W\x16m\x9c\xaa\xedX\xad\xc1\x00n\xee6\x83F\x1d\xce#VdÉ\xd1V\xb1'.N9\xed\n\xca\xe5\xcd݆}C\x97\b\x02L\xe6,9\xb3\xb5\x96\xa4\x8e\xbf\x02Ϗ\xf7\xea\xcf\x06X^\x93V\nv\xf9*\x02x\v;\x9c\xf4\x1a\x10\x066\x00\xadq\x0e\x18BM\xd5vM\x0eG\x0e;^\x17\xd6+9a\xd8\xfb\x1fY)dm\xe1\x94\xefl\x9a\xf7D$ny\xa9\x0e\xa0\x13h\xf8\x91[\xfe\v\xd6\x1d\x90\x0ea0\x02\xe2\xd9Od\xdc\x1e#\x03\xc5&['\xa9l\xb3\xeb@\x15\x86]^\xe2<\xbbt.\xf1啫[\x8b®\x84\xa4~\"0]\xefO\xa2(B\xff\xe7Q\xc3\x11\xd7\xf1\xd6ܫ\x9f\x8c\x13\xeb\x14\xe2D\x9a\x8e(\x98J\xe5\xec@\xf5b2&\n`\xe6h,\x94\x9eR\x1dυ\x88Kڱ(<\x18öǀ\xfb\xf8\xb8e]\x14|[\xc05\xb3\xba\x1e\xefvJ\x91\x8d\xd1\xe6+\x18+\xb2\x04\xca\\\x0eI\xe3Z\x8e\x10F\xd3\x0f\x11\xa2\f(\x80.\x0f\x7fD\xb7\xdbS\b}\xa7\xa2\xe8\x10w\x9e*\x8c\xfd\x8fd\x1f\xd1\xdcgh\x84\xaf\xbdq\x17P\x90C!\x15+\x94|\x00\xedzD\xc7)H\x98\x06\x94\xb8<\x02\x15-\xad\x86\x02]\x06\xb6\xab\xd1\b\xaf\x19j\x82\xa8\x8c\bi,\xf0|}\xf9Z̃\xefYQ\xe7\x90\xdf\x14\xb5\xb1\xa0\xef0\x04\xccC\b<\xaae\aL\xfc4\t\xc0\xbb_\x85\xc8\x00\xf9\x90\xb9J+\x8a4cDj=\xb1c\x05.\xf0E\xa6zL[\x17\xab\xa3*\fX\xacr\xf9\xaf\x971%\x8a\x12\xd0\xef\xbdߏa\\CC\x8d\x9eF\x8d@l\xf4,\x94\x95=\x8eˑ\xb0PF\x888\xabr\x16\xb0\x97k\xcdǔj\x18N\x13џ\xcf\xde\x18\x88\x01\x83e\xa8\xf6\x1b\xb1x\xd8\xff?\"\x93\xcfb\xab\xa1u,.$\xb2\xb3\x10\xc6\xf6\xb89\f\x88\x1a\xcc0vF\x9ab\xd0\"\xa4\x83\x89ʭü\xdf3\xcdΙ\t1\xd1o$͋\xf3\x9eDŽ\xea\x0fH\xb0\xbdR\x8f)D\xfao\xac\xd7\x06\xca,\xa3%U\xb6\x85=?\b\xa5\xcdp\xb5\x05\xbeCVۨ\x9e\xe0\x96\xe5b\xb7\x03\x8d\xb0h\x81\xb0YO\x9c\"\xd6t\x98\xc0:\n(Za0\xae\x96\xe9\xc8<\xa2Fl(\x14\x8eE\xa12B\x1c\xbdx\xb2\xee\xb98\x88\xbc\xe6\x05\x19z.37>\xde\xe0\x17sOf\x04\xe2\x04\x7f\xe7N\x84Q \x97zQ\xb6\x92\x80\xeeu\xa9t\xcc\xf3t\xe5\x14L\x9c\f[N\xc1q,$m\x8b\xae\v0\x1e\x15\xe7\xc0\xb6z\xe7\xaa\xe5\x94[\xa0*\xf8\x16\nf\xa0\x80\xcc*\x1d'O\x8a\x10\xb8\x92\xaa?#\x94\x1dѤ\xfd hV\x89\xb6\x05\x03̽\xc8\xf6\xce\xddD)#X,W`Hc\xf0\xaa*\"V\xa8-\xb3\x92\xe1;\x9bS\x1amIP\x1fC\xb81EҖD\x1dܖ\x19mܧz#6oD\xef\xa1)\x9f%웓\xe6//\xecHn\x01\x86\x9c>\U000bab98\xb0\xe1k\nԞ\x1fh\xfe\xce\x18w\xdel\xd9\f[\xbf\xf8ly\x11\xae5h\xfc\x9d0\x8d\x8c՝\xb7U\x8b\x18\xf6s\xb7\xe5\x15\x13\xbb\x86a\xf9\x15ۉ\xc2\x02\xf9Rs\x88v\x1c\x9dYν$\x81Rm/\x96\x92\xdbl\xff\xa9Y\xd6Nh1\xa0\xd5\x10\x80\xf3\xcbC\fC\f<\x9d.\n4\xc0$\x90\x9dA\x91\x9b\xd6\xc4xn?\xef\x8aq\xf6\bG\xe7Y\x8d.\x0f\x8d\x15d-o@j\xa0\xcdER#\x8fp$P~\xb70\t\xde\x12Qq\xe5\x11\x8e\xa9U\aDE\xfc\xfc>\x85\xa3.~\xa0Q\xa4L\xa5\xb64D\xf5s\x87Y\x956X\xb6L)\x85\x12(~\xe6\xb0\x1b\x86\xf5\xb6\xc8\x1f\xe1\xf8\x83q\xec\xc3Y\xb3\x17\xd5\x02\n\xa0¦%\x19\xb5k\xf6\x86\xbf\xf1B\xe4Mg4O\x16@\xdc\xc8+\xf6YY\xfc\xe7\xd3wa\x10E\x99\xb3\x8f\n\xccge\xe9˫\x92\xd8\r\xe2L\x02\xbb\xc64-\xa53\vH\x97E\xfd\xb78\x90\tE\x11m\xd8&\f\xdbH\x8c\xcf\x1c}\x96\xb0i\x0f\x019\x87VY\x1b\xda]\x96J\xaeܒ\x96\xefm\x01\xd0.^\x9eUJ\xf78u\xb5\x10\xe2(\x8a\x1e\xbd{\xb4V\ue5d3}\xf9\xa9\xa2\xa1*x\x06y\xd8e\xa3$\x00n\xe1Ad\xac\x04\xfd\x00\xacB\xbb\x91.T\v4\xb9+gHa\xbak\x11\x8a7\v#{\xdace\x85\xb3>\xb1f`sR\xf5Ȏ\xfft\xf5\xb4Q\x92y'\x7f(\x89\xfa\xdd\x14\xb5e\x96e!\xbfN}\x10\x87\xa4s?JN\x1bO\x7fE\xf3J\xe2\xfd\xb74kȅ6k\xf6\x81\x12\xf4\n\xe8\xb6\x0f\xab\x84\x9d\xae\x92@\"&\xc20\x94\x93\x03/\xd0}@\xe5-\x19\x14ΙP\xbb\x13\x0f*M\xc5<\xed\x95q6\xbf\xd9\x18\xbb|\x84\xa3ߜ\xedj\x89ˍ\x8c\xae\xda\xf7\v\xea\xfc\x13\xa5\xd5x-J\x16GvI\xbf]\x92c\xb6d\x8a\x9c\xe1\xbc-\x90\xea\x05U\xbf\xaf\x1e\xeb-h\t\x16̪\xe4\xd5\xca\xcf\x06\xab\xca\xe8\x1e\xa7+\x94F\xb7$\x8c\xc08=x<ظI6C\xf7\x7f\x8e\x02\xc9\xf3\xa1R&\x92i\x11A\xebV\x19\xeb\x16\x0f{\xae\xfa\xc8\xeabJ\xe4\xe8W\x1c\x19\xdfY\xd0\xccX\xa5Cb\x17\xaa\xec\xc1\xe2:J\x8d\x99\x97\x1b\xb7O\xe4W2\x1d`\fP/[\xed\xe2\xec\xc1\xa5۫\xc2\xff\xcf\xc3\xcc\xc8\xd1\"ؕV\x19\x98h6B[\x12\xad\xce\xccbo\xb3\xd0\xcb]\xe0\xb7KR\xeb)\xcbС,s㑴g\x04E\x9f\xbew֬Q\x85\xe1\xdf)\xa2|\x0e\x8e\x8cr\xbb˒\x0f\x93\f\x93ѽq\xad\xc3\x04\xf4\xc0\\\xb0\xa5\x1fjRH\xcb|n/\x92\xbf7\xa7\xa5\x14rC\x1d\xb1\xf7\xaf\xe6\xe8\xb0`\x06b\x19Ice\xc0\x0e߾eH\xf3!5\xf6e!UM\xd1>\x8f\x86\x1egOwA\xd29\xc5\xd0\x11\x97\xcav\x17z|O?\x18\xb6\x13\xda\xd8\x16\xe1\x05P\x85\x99\xc8z\x1a\x1d\xde\x19\xf1\xa9\xfc\xa4\xf5\xd9\xe1\xe9\x17\u05fa\xb3$\xb9WO>\xc1sIP\x1e\x88\xbf\xe7\a`bDŽe 3UKZ,Cu\x81\xdd,\x80\xe8\x98\xe8\x8cI\xa2\xcd\xec4\x96u\x99N\x90\x15I\xa7\x90\xb3+k\xdd&?q\x91\xb6\xb2\xc5\xcec\xab\x9dJ\xa2\x1c+\xfd\xccP\x9fM\xd9\xcd\xe4-\xf9wQ\xd6%\xe3%\xb2eI̹sy\x98!\xed\xd7\xf1\xfa\x89\v\xebOS\xb8M\xd9e\xda4SeU\x80\x85\x90a\x99)iD\x0e\x8d\xfb\xe0\xf9?\x9a\xaf\x1a+\x9c\xed\xb8(j\xbd@G/\xe6\xccҘϫ\xa7\x97\x0f\xe4\xd2\x11Y\x111\x13\x17\xec\x178\xdc\xf3\xf6\xa3\xd2\xcb\\\xe6[\r/\xef\x9aVZ(ʁ\x9d\xf1Nga\x92\xf7\xda\xf7N\xbd\xf0ry\x8c\xb9\xa7\xb3P\t\x937\xf7\xb4)o\xee\xe9\x9b{\xfa\xe6\x9e\x0eʛ{\xfa枾\xb9\xa7\xe3\xe5\xcd=\xed\x947\xf74\xd9~\xa4`\xb8\xa2\x95ۉ\nIX%\xa6o̡=ӗ\xcfR\xf2gA\x96dWo\xc6[\x8e\x9c\x05Zt\x86\xc4t\x8c^\x93n\x8dS2L&w\xa64\xc1\v\x7f\x81\xb36\x01\x81\xb3\xcf\xdal&\x01\xbc\xe0Y\x1b\x8f\xe9p\xed\xfc\x05O\xda\x04Z,?\x84q\xe5ӘJ\xe0aK\xc8\xe5\xa0\xe4\xb1nc^l\x0f\x8f\xd1:\xbfq\xd6\xfdI\xb6\xe6\xf9\"\xf3\xffr~'\"6'\xa7S#P\x85A\xb9\xfacp\xe2,\xdaG\xa9\xed\xfe\x17\x1b]KX\xa7x\xdd5\x03\xddT\xcb~\xca\xeb\x1fG\xb0ϑ\xe4\xd4\xf371\xe7<\xae\xdb:\xc4Խ\xf3\x1e\xbfoZZ(\xbfTޒ\xa5\x9f{ߌ4{\xc6\xc9wn\x8e2\xdbk%Um\xfc\n\x0f\xf6\xf0!sW\x01\x84\x8e\xcc\x12e\xf0\x9e\xedU\x1d9\xe31Cׄ\xcc\xdbx\xbe\xad\xcf\xe0\x00\xcb\x0f\xef\xd7\xfd_\xac\xf2ٷ\x11\xac\x9f\x84ݻ\xfb\x18x\x9e\xa3\xa3\xde9\xe2\x13&\xaf\xbf\x93e(x\x11\x88J3)\n'\x95\x01B߀~\xa9ܒ\xdf\xd9~\xcb\xfc\xc2Sz\x8e\xee\xd2\xcc\xdc&\x97r\xdeK~F>\xee\xb2\xc3R\xb3\xb9\xb7)H\xb3\x94\x8c\xdb\xf1\\\xda\x19\xa8K\xf2lS\xd7\x14\x13rj\xd33i\xd3\xc8\xc3\xe8&\xa5\xd4\xfc\xd9\xe4(45W\xf6u2d\x13\xf3b;ٮ\xb3 \xcf̆M&XZ\xe6kr\xbek'\x8bu\x9eZ\x13Y\xae㹫\xb3 \xc7r[S2V\x93pM\xceSm\xb2O\xe7wF\x9e\x95\x9d\xfa\xf2\xe7`^r\xddb:\xd74)\xc34imc\x1e\xe7\xa4\x1cҥ\x99\xa3IT]\x9a%\xdad\x80Nt\x9c\x94\x1bz\x9a\xf795\x94ٌ\xd0x\xb6\xe7\x14ر<Є\x1c\xcf\t\x90\xdd\xec\xcf\xc5n\xc0\xac4\xcdVX\x9a\xbb9~?Z(\xf3ֹ\xf8-d\xf6\xb9dR\xba\xe74\xa7\x04w_\x06MPZ\x82\x9f8\xe6\x88\xc7Ce瞟\xe1\x88G@nv\xac\xac\v+\xaa\xa2sA\x99\xddñ\xb9\xf2\xe7WE\a\u05f7G\x82\xf6\xe5k#\xf21\x90\xfd\x90\x82\x1b\xf6\x04E\x81\xff\x9eP!s\xd7\x01fj\x05h\xa5\xe2\x1b\x81\xfe\xaa#\x7f\x97\xe0\x95[\x16\xa3S\xfdd\x01K\x844}\x01֤)\x99v\x8f\x9dWO\xdf\xfeR\x83>2\xbas+\xf8AQ1kO{\xfa\xc9l0&\f\xca\xc7k1w)e_\x19\xc5gC\xa3\x02\xd8\a\xe9\f\xf3\x10W\x82\x85Z\xa7\r\xa7\xa6\x94-FO1\x10R5\x10\"\xedS\xbc\xef%\xc7\x1f_#\xb8z\x89\xf0*\xc9\x11y\x8d\x10뵂\xac\xa5a֒䍤㋯\x11l-\t\xb7\x16\xf9\x8c\xe9\xc7\x13_\xebX\xe2+\x84]g\a^\x8bH\x97z\xecpq\xf8\x950\xbe\x99c\x86'>Z\x02\xc8\xe8\xf1\xc2\xf1\x10,\x01\xe2ɱ\xc2\xd9 ,e\x1e\fôg\x1f\x12LNdZ\xb4\x9b\x9e\x9a\x84\x94\xb6\xd1=\x7f\xf8/\xf1\xd0_\xe26x\n\xf6\x89\x87\xfb\x96\x1f\xeaK\xa4\xf3\x99\xe1\xd9d\u05c9\x87\xf7\x16\x05hg\x86h\x93\x10\xa7\x0e\xebM\ai\xd3\vp\xc3Czg\xb8\x13\t\x12\x96Pe\xf9A\xbbgo\xc6(\x9d\x83\x9e\xdd\xd7Z\"γ\x82<\x88\xa3\xfa\xfd\x0fvt\u008d\xa8X\xab\xbbg\x16\xe3\xa8j\xee\x1d\xc9؟\x84\xf4\xbb\xf5(\xb8\x1d\x9f\xa4\xb7\xf1\xd6:L\xf1}\x9d\xd6K\xf5\x17\xab\xbb\x1d;\x03\x15״\x8f\xbf=\xba\xa4 \xb3f\x9fx\xb6oz\x88\x80\xa4~\xf7ܰ\x9d\xd2%\xb7\xec\xb2\xd9\n}\xe7:\xc0\xbf/\u05cc\xfd\xa4\x9a\xf4\x91νb\x11\xa8F\x94UqĈ\x89]v\xc1ti^\xd6ɂ\x88\xc5(\xd8\\\xf8\v\v\a\x97\n\xefTQ\xa8\xa73\x97.x%\xfe\x8b\xde1I[\x1b\xfbp\xbb\xa1\xeaA\xaa\xe8\r\x94&{\xae\x91\xb1-L+\xf4v\xe0\xe4zt\xa1\x8ed\xaf6\x7fN@\xa4W\x04\x82\x9f\xe1\xd5x\xa6P\x8b\xddn\x1c\x96k\x12,.\x8fL\xf9{\xe2\x85\xceW\x15\xd7\xd1M=\xe6\xe5\xc1\\\xf50\fv|n\x05kҬ\x9d\xbe\x8a\xd0-=\x9a\x87\a\x12h\xb3\xf7X\xf5\xb7щ\xd2\x1dz>\a\xa7\xe9\x83˳G\x96_\x01\xa7i\x97iET\x8c\xfc\x14M\xc7{\xf1\xd5C\xe3/\x91\xffE\x1d\xe0ct\x15\xb1\xffd\xc0\xa0\xc9H\x02]\x80:umz\x9b5\x17\xbf\xce\xfa\x052\xe2\x02*\xfe\xe2\xeb\x05\xe3\xf3-\xc6^i\xf0\xf7\x7f\a\xd8\x13\xb6\r\xa7\xec\xed7\n7\x1bu\xe9g\xb8\x0f&\xc3R\xe1\xe0\xaa\xd6\b\xc8\xd83\v/E-\xab4\x7f\x80\x9f\x95{\t#\x85Z\xfd\x16\xbd\xc7P\xbc3\x17\xb2\x89\xfd\\\x8b)s?\xb6!\xc0\xf6\x90\xc1\xc9u\xf4\x88홷\xed[[$\f\xee\xfe\xfeg7 +JX\x7f\xac]\x86\t\xea]\x03H\xe90P\xd7h\x1b\xd7N{\xf5D\xf7\xb9w\x9f\xab\xe8<\b\x04t\xa8\x81\xd2F\xcf\x1aM]\x15\x8a\xe7\xa0o\x94܉\x87\x84\x81\xfd\xb9\xd7``\xd93\xfa\xe8\a\x1b\xeccd`\xa1\xe7W\xcc\fA\x97\xad(\xa0\xf8I\x14`\x1c≊\xfe\xf6\xb4e\xa3\xf7\xebr\xeb\x1c\xd4\x1d\xfe\xd8t2aM\xddPiM\xbf\x02\x8d\x8e\xa0[\xfd\xafM\x10\xf0ib\xb0\x86\x8fBZx\x88$\xf8\xcch\xf8C\xef\xe9\x8f0IR\xd4ڷ\xf1\x96\x1do\xb93]\xa7\x92\x05\xd5.\n\x8b\x1b\xa32A\x0e6\xed\x8eЩ\x91\u05fbsz*P\x9a\xa0cm\xe0˓\x04\xfd5\xa8d\xb3\x91\xb1\xb76\xfa\xb3\xe6\xa4a\xf4\x9d\r\xabȭ\x1fT\x1f\v\xe7\xa4'\x90q\xaf\xb4\x84m\x1ea\x9a\xb7yNI73\xaf\xe2Z~\xdc'Y\x8d?\x87\xb3j^\xe8\xb9H\xa0\xac{\x85\xa6\x0fx\xfc\xf1%\xf7\\M\xc6+[\xeb\xa0rjM\x17p#\x10p\xf7S\x9f\xf7\xfcR\xfb\x9c\xdd\f/\xdb\a\xeeڅ\xef\xd9\xe7\xf4F\xf8\xd7<\xa0\x14}Qȅ\x8c\uee7b\x15\xc2?\x8f\x9d\xa3\xf3\x80.,\x9f\x19\xe9-\xd6iN\xfcyBS\xc3p\xd1\xf9]\f\xf5\xf1#\\+\xf6\x19NC\xb6\x15\xfb$q\x10\xa7\x9e\x9c;\xa7\x059-\x97\x8f==79\xc4Cӊ\x0eɍh\x8b\xbe\x9a\x1bT\x1f\xa4\xf0\xd2\xc35M\x15w n\x8c\xad\xff,vn/#\xc31\xfd\xcbI\x8d\xa8\xe2\x9aTZ1\x855:\xa5N>\x1a\xd0\az)&\b\x89\xf7ֺ_\xeam{\xef<\xfb\xeb\xdf.\xfe/\x00\x00\xff\xff\x89~\x02\xaf\x9ft\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec=Mw\x1c)\x92w\xfd\n\x9e\xf6\xd03\xfbT\xe5\xf6\xeee\x9fn^ٽ[oܶ\x9e\xa5\xf1i\x0fCeF\xa9heB\x0e\x90%\xd7Λ\xff\xbe/\x02ȯJ2ɲ\xb4\xdd=#.\xb6\xb2 \b\"\x82\xf8\x80\x00V\xab\xd5\x05\xaf\xc4W\xd0F(y\xcdx%\xe0\x9b\x05\x89\x7f\x99\xf5\xe3\x7f\x98\xb5Po\x0eo/\x1e\x85̯\xd9Mm\xac*\xbf\x80Q\xb5\xce\xe0=\xec\x84\x14V(yQ\x82\xe59\xb7\xfc\xfa\x821.\xa5\xb2\x1c?\x1b\xfc\x93\xb1LI\xabUQ\x80^=\x80\\?\xd6[\xd8֢\xc8A\x13\xf0\xd0\xf5\xe1\xc7\xf5\xdb\x7f[\xffx\xc1\x98\xe4%\\3\x93\xed!\xaf\v0\xeb\x03\x14\xa0\xd5Z\xa8\vSA\x86@\x1f\xb4\xaa\xabk\xd6\xfe\xe0\x1a\xf9\x0e\x1d\xb2w\xbe=}*\x84\xb1\x7f\xea}\xfe(\x8c\xa5\x9f\xaa\xa2ּ\xe8\xf4G_\x8d\x90\x0fu\xc1u\xfb\xfd\x821\x93\xa9\n\xae\xd9'\xec\xaa\xe2\x19\xe4\x17\x8cy\xfc\xa9\xeb\x15\xe3yN\x14\xe1ŭ\x16҂\xbeQE]\x06J\xacX\x0e&Ӣ\xb24\xe2;\xcbmm\x98\xda1\xbb\x87n?X~1J\xder\xbb\xbffkC\xf5\xd6՞\x9b\xf0\xab#\x91\x03\xe0?\xd9#\xe2f\xac\x16\xf2a\xac\xb7w\xecF+\xc9\xe0[\xa5\xc1 \xca,'\x06\xca\a\xf6\xb4\aɬb\xba\x96\x84\xca\x7f\xf2챮F\x10\xa9 [\x0f\xf0\xf4\x98\xf4?\xce\xe1r\xbf\aVpc\x99\x15%0\xee;dO\xdc\x10\x0e;\xa5\x99\xdd\v3O\x13\x04\xd2\xc3֡\xf3q\xf8\xd9!\x94s\v\x1e\x9d\x0e\xa8 \xbc\xebL\x03\xc9\xed\xbd(\xc1X^\xf6a\xbe{\x80\x04`D\xa2\x8a׆\x84\xa3m}\xdb\xfd\xe4\x00l\x95*\x80ˋ\xb6\xd2᭓\xbdl\x0f%\xbf\xf6\x95U\x05\xf2\xdd\xed\xe6\xeb\xbf\xdf\xf5>\xb3\x81,yJ1a\x18g_ib0\xedg*\xb3{n\x99\x06\xe4?\xa1 \xf7tp\xf3C\x98\x16\x7fbR\x0f0\xc3J\\2\xb5\xfd\x052\xbbfw\xa0\x11L\xc0:S\xf2\x00\x1a)\x90\xa9\a)\xfe\xb7\x81mP\xea-\t\xa3\x05\xaf\x0f\xdaB\x13X\xf2\x82\x1dxQ\xc3\x15\xe32g%?2\r\xd8\v\xabe\a\x1eU1k\xf6\xb3\xd2\xc0\x84ܩk\xb6\xb7\xb62\xd7o\xde<\b\x1b4i\xa6ʲ\x96\xc2\x1eߐR\x14\xdb\xda*m\xde\xe4p\x80\xe2\x8d\x11\x0f+\xae\xb3\xbd\xb0\x90\xd9Z\xc3\x1b^\x89\x15\xa1.I\x9b\xae\xcb\xfc_\x02G\xcd\x0f=\\O\xe6\x9b+\xa4\b'8\x80\x1a\xd1\t\x8ck\xeaF\xd1\x12\x1a?!u\xbe|\xb8\xbb\xef\n\x930C\xea\x13\xdd;\x12ֲ\x00\t&\xe4\x0e\xfc\x8c\xdeiU\x12L\x90y\xa5\x84\xb4\xf4GV\b\x90C\xf2\x9bz[\n\x8b|\xffk\r\xc6\"\xaf\xd6\xec\x86\xcc\v\xcaa]\xe1\f\xcc\xd7l#\xd9\r/\xa1\xb8\xe1\x06^\x9c\x01Hi\xb3B¦\xb1\xa0k\x19\x87\x95\x1d\xd5:?\x04\xf3\x16\xe1W\x98\xe3w\x15d\xbd)\x83\xed\xc4Nd41H{6*`\xa0A]\x19\x9f\xb5\xf4\v\xa9\xa9\xe1\xd7\x01\x1eN\x97\x85^\xc1\xa0\xfd\xb0{\xe2pk\xc6P\xae\x1c4\xd4)R\r\xb9;\xa6\x05;\x94\xf0Pf0\xe9k\xbdT\xfbv\x02\x93yU\xb7\x8e\xe0x\xc2UB\xf1QT\x9b\xb2\x84\\p\v\xc5q\f\xd3\x1d\xaf\v{\xcdv\xbc0\xa7\x9d\xf6\xa7\xe2]\x1f\xda\x18q\x15uɶN+\x8b\xdd\xc80\xba\xc4\xcfk`\xa2\x03\x91f\xde_B\x8dS\x03\xfa\x172\xc6]\xbb\xd7-D\xc2.\xf8Z\xb6\xdc\x15\xbb\xdeO\x12\x9e\xd6l\xb3cV\xa3\xd6\xdcv\xedp\x0f\xa4(\n\x9c\xc88\xaa\n\xf2\x1e\xb2\xf1\xeeĎ\t\xeb\xc77\x02t\xcb\t'\xc9\xd6\xce9Z\xb7\xae@c\xd6\x11\xe5\x01\xbeN\xb9#F#0Ql\xb8e\x12\xbeٶ\x1d\x12\x8bFI\xdc\r\xc3t\x83\xf2\x1a\xca\x0fl\x04b\xd2P\xafض\xb6\x0e\xe0\x18\x06#`\x1b\x9c\xa0\xac\xec\xf1ʵݩ\xa2PO̐>FS\xb8\x13\x0f\xb5v\xaa\xe2\x0f}\x19\xfd\xe3\xfa\x87E\xb3\xd4BY\xa1圙\xa5\xf7\xbe\x1a\x0e\x0eUD\xde\x04\x0e\xc1\xf7\r\x9e\x86\xf2\x0e\x06;\xb1\xef\xd4\xdd\x1ePu\x1dD\xee\r\xf8(\x19\xe2\xca\rKfĝ\xe4\x95\xd9+\x8b\xf2\xa0j;Vk0\x80\x9b\xbb͠Qg~\"V\xe4ƒxZŞ\xb88Uv\xae\xa0j\xbe\xb9۰\xaf\x18\x15@\x80\xc9\xdc\\d\xb6֒<\x92/\xc0\xf3\xe3\xbd\xfa\xb3\x01\x96\xd7d\x98\x83kz\x15\x01\xbc\x85\x1d\xda=\r\b\x03\x1b\x80\xd6h\x06\f\xa1\xa6j\xbb&\x9f۳\xdb\xdbya\xd8\xdb\x1fY)dm\xe1T\xf5\xb1i\xf5GD▗\xea\x00:\x81\x86\xef\xb9\xe5?c\xdd\x01\xe9\x10\x06# \x9e\xfdD\xc6\xed12Pl\xe2\xa6\x1a\tz\vU\x18vy\x893\xfb\xd2E\x85\x97W\xaen-\n\xbb\x12\x92\xfa\x89\xc0t\xbd\au\x84\xfd\x9fG\rG\\\xc7[s\xaf~2N\xacS\x88\x13i:b\x06*\x95\xb3\x03Ջɘ(\x80\x99\xa3\xb1P\x06\xa5\xd4:\xefD\\r\x10\x8a\u00831l{\f\xb8\x8f\x8f[\xd6E\xc1\xb7\x05\\\x93F\x9f \u0378\x96\x18\xa3\xcd\x170Vd\t\x94\xb9\x1c\x92Ƶ\x1c!\x8c\xa6\x1f\"D\x19P\x00\xbd~\xfe\x88\x91\xa7\xa7\x10\x86\x0fE\xd1!\xee\xbd\x8f\xad$\xf6K\xa2\xa4\x9e\f\xea\xdd\xc0\xd3\xe9\xa2@\x03L\x02\xd9\x19\x14\xb9iM\x8c綴\xaf\x18g\x8fpt\x9e\xd5\xe8\xf2\xd0XA\xd6\xf2\x06\xa4\x06\xda_'5\xf2\bG\x02\xe57̓\xe0-\x11\x15W\x1eadC.VzDE\xfc\xfc>\x85\xa3.~\xa0Q\xa4L\xa5\xb64D\xf5s\x87Y\x956X\xb6L)\x85\x12(~\xe6\xb0\x1b\x86\xf5\xb2D\x1e\xe1\xf8\x83q\xec\xc3Y\xb3\x17c\xfbx\xd1A(\f}\x81fXH\x8f\xf8\xca\v\x917\x9d\xd1\xa4ƾ,\xa4\xaa)\xda\xe7\xd1\xd0\xe3\xec\xe9.H:\xa7\x18:\xe2R\xd9\xeeB\x8f\xef\xe9\a\xc3vB\x1b\xdb\"\xbc\x00\xaa0\x13YO\xa3\xc3;#>\x95\x1f\xb4>;<\xfd\xecZw\x96$\xf7\xea\xc9\xe78/\t\xca\x03\xf1\xf7\xfc\x00>\x95\x14d\xa6jI\x8be\xa8.\xb0\x9b\x05\x10\x1d\x13\x9d1I\xb4\x99\x9dƲ.\xd3\t\xb2\"\xe9\x14rve\xad\xdb\xe4'.\xd2V\xb6\xd8yl\xb5SI\x94c\xa5\x9f\x19\xea\xb3)\xbb\xc9\xec%\xff&ʺd\xbcD\xb6,\x899w.\x0f3d\xbe;^?qa\xfd\x81\"\xb7)\xbbL\x9bf\xaa\xac\n\xb0\x102,3%\x8dȡq\x1f<\xffG\xf3Uc\x85\xb3\x1d\x17E\xad\x17\xe8\xe8ŜY\x1a\xf3y\xf5\xf4\xfc\x81\\:\"+\"f\xe2\x82\xfd\x02\x87{\xde~Tz\x99\xcb|\xab\xe1\xf9]\xd3J\v\xe5\xb2\xfc\xa7\xbd\xd3Y\x98\xe4\xbd\xf6\xbdS/\xbc\\\x1ec\xee\xe9,T\xc2\xe4\xd5=mʫ{\xfaꞾ\xba\xa7\x83\xf2Ꞿ\xba\xa7\xaf\xee\xe9xyuO;\xe5\xd5=M\xb6\x1f)\x18\xaeh\xe5v\xa2B\x12V\x89\xe9\x1bsh\xcf\xf4峔\xfcY\x90%\xd9՛\xf1\x96#g\x81\x16\x9d!1\x1d\xa3פ[\xe3\x94\f\x93ɝ\x95L\xf0\u009f\xe1\xacM@\xe0\xec\xb36\x9bI\x00\xcfx\xd6\xc6c:\\;\x7fƓ6\x81\x16\xcb\x0fa\\\xf94\xa6\x12x\xd8\x12r9(y\xacۘ\x17\xdb\xc3c\xb4ί\x9cu\x7f\x92\xady\xbe\xc8\xfc\xbf\x9c߉\x88\xcd\xc9\xe9\xd4\bTaP\xae~\x1f\x9c8\x8b\xf6Qj\xbb\xff\xc5F\xd7\x12\xd6)^w\xd3F7ղ\x9f\xf2\xfa\xfb\x11\xecs$9\xf5\xfcM\xcc9\x8f\xeb\xb6\x0e1u\xef\xbc\xc7o\x9b\x96\x16\xcaϕ\xb7d\xe9\xe7\xde7#;\xe3\xe4;7G\x99\xed\xb5\x92\xaa6~\x85\a{x\x97\xb9\xab\x00BGf\x892x\xcb\xf6\xaa\x8e\x9c\xf1\x98\xa1kB\xe6m<\xdf\xd6gp\x80凷\xeb\xfe/V\xf9\xec\xdb\b\xd6O\xc2\xeeݭ\x0f<\xcf\xd1Q\xef\x1c\xf1\t\x93\xd7_K4\x14\xbc\bD\xa5\x99\x14\x85\x93\xca\x00\xa1o@?Wn\xc9\xefl\xbfe~\xe1)=Gwifn\x93K9\xef%\x7fG>\xee\xb2\xc3R\xb3\xb9\xb7)H\xb3\x94\x8c\xdb\xf1\\\xda\x19\xa8K\xf2lS\xd7\x14\x13rj\xd33i\xd3\xc8\xc3\xe82\xb1\xd4\xfc\xd9\xe4(45W\xf6e2d\x13\xf3b;ٮ\xb3 \xcf̆M&XZ\xe6kr\xbek'\x8bu\x9eZ\x13Y\xae㹫\xb3 \xc7r[S2V\x93pM\xceSm\xb2O\xe7wF\xbe+;\xf5\xf9\xcf\xc1<\xe7\xba\xc5t\xaeiR\x86i\xd2\xda\xc6<\xceI9\xa4K3G\x93\xa8\xba4K\xb4\xc9\x00\x9d\xe88)7\xf44\xefsj(\xb3\x19\xa1\xf1l\xcf)\xb0cy\xa0\t9\x9e\x13 \xbbٟ\x8b݀Yi\x9a\xad\xb04ws\xfc\x8a\xc0P\xe6\xads\xf1k\xc8\xec\xf7\x92I\xe9\x9eӜ\x12\xdc}\x1e4Ai\t~\xe2\x98#\x1e\x0f\x95\x9d{~\x86#\x1e\x01\xb9ٱ\xb2.\xac\xa8\x8a\xce\x05ev\x0f\xc7\xe6ʟ_\x14\x1d\\\xdf\x1e\t\xda\xe7/\x8d\xc8\xc7@\xf6C\nn\xd8\x13\x14\x05\xfe{B\x85\xcc݈\x99\xa9\x15\xa0\x95\x8ao\x04\xfa\xab\x8e\xfcu\x9aWnY\x8cN\xf5\x93\x05,\x11\xd2\xf4\x05X\x93\xa6d\xda=v^=}\xfbk\r\xfa\xc8\xe8έ\xe0\aEŬ=\xed\xe9'\xb3\xc1\x980(\x1f\xaf\xc5ܽ\xac}e\x14\x9f\r\x8d\n`\xef\xa43\xccC\\\t\x16j\x9d6\x9c\x9aR\xb6\x18=\xc5@H\xd5@\x88\xb4O\xf1\xbe\x97\x1c\x7f|\x89\xe0\xea9«$G\xe4%B\xac\x97\n\xb2\x96\x86YK\x927\x92\x8e/\xbeD\xb0\xb5$\xdcZ\xe43\xa6\x1fO|\xa9c\x89/\x10v\x9d\x1dx-\"]\xea\xb1\xc3\xc5\xe1W\xc2\xf8f\x8e\x19\x9e\xf8h\t \xa3\xc7\v\xc7C\xb0\x04\x88'\xc7\ng\x83\xb0\x94y0\fӾ\xfb\x90`r\"Ӣ\xdd\xf4\xd4$\xa4\xb4\x8d\xee\xf9\xc3\x7f\x89\x87\xfe\x12\xb7\xc1S\xb0O<ܷ\xfcP_\"\x9d\xcf\f\xcf&\xbbN<\xbc\xb7(@;3D\x9b\x848uXo:H\x9b^\x80\x1b\x1e\xd2;ÝH\x90\xb0\x84*\xcb\x0f\xda}\xf7f\x8c\xd29\xe8\xd9}\xad%\xe2<+ȃ8\xaa\xdf\xff`G'܈\x8a\xb5\xba{f1\x8e\xaa\xe6ޑ\x8c\xfdIH\xbf[\x8f\x82\xdb\xf1Iz\x1bo\xad\xc3\x14\xdf\xd7i\xbdT\xff\xb6\x80۱3PqM\xfb\xf8ۣK\n2k\xf6\x81g\xfb\xa6\x87\bH\xeaw\xcf\r\xdb)]r\xcb.\x9b\xad\xd07\xae\x03\xfc\xfbr\xcd\xd8O\xaaI\x1f\xe9\xdc+\x16\x81jDY\x15G\x8c\x98\xd8e\x17\xcc\xf7\tNT`\x03>\xb7\xaa\x10Y\xc4C\x1c\xbd\x92\xce58\xb9Ї.\xcd\xcb:Y\x10\xb1\x18\x05\x9b\v\x7fa\xe1\xe0Raw\xbd\xf8\x99K\x17\xbc\x12\xffEO\xf9\xa4\xad\x8d\xbd\xbb\xddP\xf5 U\xf4\fP\x93=\xd7\xc8\xd8\x16\xa6\x15z;pr=\xbaPG\xb2W\x9b?' \xd2C\x1a\xc1\xcf\xf0j\xb7\x825i\xd6N\x1f\x06\xe9\x96\x1e\xcd\xc3\x1b!\xb4\xd9{\xac\xfa\xdb\xe8D\xe9\x0e=\xbf\a\xa7\xe9\x83˳G\x96_\x00\xa7i\x97iET\x8c\xfc\x14M\xc7{\xf6\xd5C\xe3/\x91\xffY\x1d\xe0}t\x15\xb1\xffjƠ\xc9H\x02]\x80:umz\x9b5\x17\xbf\xce\xfa\x192\xe2\x02*\xfe\xe2\xeb\x05\xe3\xf3-\xc6\xdfҠ\xfb\xbf\x03\xec\tۆS\xf6\xf6+\x85\x9b\x8d\xba\xf43\xdc\a\x93a\xa9ppUk\x04d쥑碖U\x9a?\xc0G\xe5\x1e\x83I\xa1V\xbfE\xef= \xef̅lb?\xd7b\xca\u070fm\b\xb0=dpr\x1d=b{\xe6m\xfb\xd6\x16\t\x83\xbb\xbf\xff\xe8\x06DOc\xbc\xf7\xef^\xa0\xde5\x80\x94\x0e\x03u\x8d\xb6q\xed\xb4WOt\x9f{\xf7\xb9\x8aΛX@\x87\x1a(m\xf4\xac\xd1\xd4U\xa1x\x0e\xfa\x86\x9e\xe7H\x18؟{\r\x06\x96\xbd\xffȇ\xb7\x8f\x91\x81\x85\x9e_03\x04]\xb6\xa2\x80\xe2'Q\x80q\x88'*\xfa\xdbӖ\x8dޯ˭sPw\xf8c\xd3Ʉ5uC\xa55\xfd\n4:\x82n\xf5\xbf6A\xc0\xa7\x89\xc1\x1a>\ni\xe1!\x92\xe03\xa3\xe1\x0f\xbd\xa7?\xc2$IQk_\xc7[v\xbc\xe5\xcet\x9dJ\x16T\xbb(,n\x8c\xca\x049ش;B\xa7F^\xee\xce\xe9\xa9@i\x82\x8e\xb5\x81\xcfO\x12\xf4\x97\xa0\x92\xcdF\xc6\xde\xda\xe8Ϛ\x93\x86\xd1w6\xac\"\xb7~P},\x9c\x93\x9e@ƽ\xd2\x12\xb6y\x84i\x9e\xa7:%\xdd̼\x8ak\xf9q\x9fd5\xfe\x1cΪy\xa1\xe7\"\x81\xb2\xee\x15\x9a>\xe0\xf1\xf7\xc7\xdcs5\x19\xafl\xad\x83ʩ5]\xc0\x8d@\xc0\xddO}\xde\vd\xed\x83T3\xbcl\x1fvj\x17\xbeg_\x94\x1c\xe1_\xf3\x86X\xf4E!\x172\xba\x17\x1fW\b\xffH\x1c\xc4)\x01܉3\xc8i\xe1\x7f\xec\x1d\xc9\xc9!\x1e\x9aVt\xdcoD\xef\xf5\x15\xf6\xa0\xfa \x19\x99\x9e\xe0i\xaa\xb8\xa3}c\x02\xfa\a\xb1s\xbb2\x19\x8e\xe9\x8f'5\xa2*xR\xfd\xc6T\xef\xa8r8\xf9H\xaf\x89\xe5\x1d!\xf1~g\xf7K\xbdmo\xd0g\x7f\xfb\xfb\xc5\xff\x05\x00\x00\xff\xfftg\x9c\xc5lx\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4V\xcfo\xeb6\f\xbe\xe7\xaf \xb0û\xcc\xce\xebv\x19r\x1b\xba\x1d\x8am\x0fE\xf3л\"\xd3\tWY\xf2H*]\xf6\xd7\x0f\x92\xec&\xb1\x9d\xb5\x1b0\xdd\"\xf1\xc7Ǐ\xe4\xe7TU\xb52==#\v\x05\xbf\x01\xd3\x13\xfe\xa9\xe8\xd3/\xa9_~\x90\x9a\xc2\xfax\xb7z!\xdfl\xe0>\x8a\x86\xee\t%D\xb6\xf8\x13\xb6\xe4I)\xf8U\x87j\x1a\xa3f\xb3\x020\xde\a5\xe9Z\xd2O\x00\x1b\xbcrp\x0e\xb9ڣ\xaf_\xe2\x0ew\x91\\\x83\x9c\x83\x8f\xa9\x8f\x9f\xeb\xbb\xef\xea\xcf+\x00o:܀ \xa775\x1a\x85\U0004f222R\x1f\xd1!\x87\x9a\xc2Jz\xb4)\xfe\x9eC\xec7p~(\xfeC\xee\x82{\x9bCms\xa8\xa7\x12*\xbf:\x12\xfd\xe5\x96ů4X\xf5.\xb2qˀ\xb2\x81\x1c\x02\xeb\x97s\xd2\nD\xb8\xbc\x90\xdfGgx\xd1y\x05 6\xf4\xb8\x81\xec\xdb\x1b\x8b\xcd\n` $Ǫ\x06.\x8ew%\x9c=`gJ\x12\x80У\xff\xf1\xf1\xe1\xf9\xfb\xed\xd55@\x83b\x99zʹ.T\x06$``@\x01\x1a\xc0X\x8b\"`#3z\x85\x82\x12ȷ\x81\xbb\xdcɷ\xd0\x00f\x17\xa2\x82\x1e\x10\x9e3\xe5Ce\xf5\x9bIϡGV\x1a\xd9\x18\xdc\xceCvq;\xc1\xfa)\x95S\xac\xa0IӅ\x923\r\x94`30\x00\xa1\x05=\x90\x00c\xcf(\xe8u\x8a2\xf3ӂ\xf1\x10v\xbf\xa3\xd5z\xe0AR\xb3\xa2k\xd2P\x1e\x91\x15\x18m\xd8{\xfa\xeb-\xb6$BRRgt\x9c\x93\xf3!\xaf\xc8\xde88\x1a\x17\xf1[0\xbe\x81Μ\x801e\x81\xe8/\xe2e\x13\xa9\xe1\xb7\xc0\x98\xc9\xdc\xc0A\xb5\x97\xcdz\xbd'\x1d\x97ˆ\xae\x8b\x9e\xf4\xb4\xce{B\xbb\xa8\x81e\xdd\xe0\x11\xddZh_\x19\xb6\aR\xb4\x1a\x19צ\xa7*C\xf7y\xc1\xea\xae\xf9\x86\x87u\x94OWX\xf5\x94&K\x94\xc9\xef/\x1e\xf2B\xfcC\a\xd2:\x94\xf9(\xae\xa5\x8a3\xd1\xe9*\xb1\xf3\xf4\xf3\xf6+\x8c\xa9s3\xa6\xecg\xdeώrnA\"\x8c|\x8b\\\x9a\xd8r\xe8rL\xf4M\x1fȗ鲎\xd0O闸\xebHe\x9c\xddԫ\x1a\xee\xb3\xe2\xc0\x0e!\xf6\x8dQljx\xf0po:t\xf7F\xf0\x7fo@bZ\xaaD\xec\xc7Zp)\x96S\xe3\xc2\xda\xc5\xc3(s7\xfa\xb5\xb0\xdd\xdb\x1em\xea`\"1ySK6\xaf\a\xb4\x81\xc1,\xb9\xd4\x1fB\x92=\xfe%\x96AI\n\x9a\x89\xbe\xa4\xfd|\x1fͲ\x9c䗃\x11\x9c^N0=&\x9bi~G-ړuXB\x145\xc1\xf7\xa1\xa4\x83>v\xf3\x9c\x15|\xc1ׅ\xdbG\x0eIY\xb3\xae_\x9f\x1b\xb3\x01\xe5{\xb3'?+wZY\xb1\xca߰K\xa9\xbe\x10\xe8!\x10p\xf4>\xed\xedL!3\x90\xa9\x92\xcflH\xb1[@\xb3\x88\xe7\xc1\xb7!\x7f\xf0MJl\xb4\xec\x13\x0e\xcd\x1e\xf2\x14\\\v\x01o\xf7\xba\x9c\xb9x}\x88\xd0r\xf2\x97\xf4\xbf9'\xb9!\xc6\xc5\xdcUF\xb5\xf8\x902.1\xbe\xbc_\x03\xca\xe8\x9c\xd99܀r\x9c{\x17_\xc3lNө\x19G\xed+u(j\xba\xfe\xbd\x01\x9a9\xa4=y=\xa0\xbf\xb5\r\xf0j\xa6*\x7f\x95\x19v\xa7[\xae\xf7o\xff\x01\xe7+UFw\x03I\xbb+\xa5\x05\xce>D\xcab\xf7\xcaH/\xfe\xf3\x98\x11\xb2\xbd\xb4\x1d5\xe3j5\xc6?\"\xf3\x1anBXl\xf6\xec2\x87o.\xca\x13\rl\xf6c\xc1\x7f\a\x00\x00\xff\xff\xb1J-\xe7\xa6\v\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VA\x93\xdb6\x0f\xbd\xfbW`&\x87\xbdDr\xf2}\x97\x8e/\x9d̦\x87L\x93f'N\xf7N\x8b\x90\x8d\x9a\"U\x10\xd4\xc6\xfd\xf5\x1d\x90\xd2\xdak\xcb\xc9n\xa7\xd5\xc5c\n\x04\x1f\xde\xc3\x03UU\xd5\xc2\xf4t\x8f\x1c)\xf8\x15\x98\x9e\xf0\x9b\xa0\xd7\x7f\xb1\xde\xff\x14k\n\xcb\xe1\xedbOޮ\xe06E\t\xdd\x17\x8c!q\x83\xef\xb1%OB\xc1/:\x14c\x8d\x98\xd5\x02\xc0x\x1f\xc4\xe8rԿ\x00M\xf0\xc2\xc19\xe4j\x8b\xbeާ\rn\x129\x8b\x9c\x93OG\x0fo\xea\xb7\xff\xab\xdf,\x00\xbc\xe9p\x05Cp\xa9\xc3\xe8M\x1fwA\\hJ\xcez@\x87\x1cj\n\x8b\xd8c\xa3Gl9\xa4~\x05\xc7\x17%\xc5x|\x81~\x9f\xb3\xad\xc7l\x1f\xc7l9\xc0Q\x94_\xbf\x13\xf4\x91\xa2\xe4\xc0\xde%6\xee*\xb2\x1c\x13w\x81\xe5\xb7\xe3\xe9\x15\fѕ7\xe4\xb7\xc9\x19\xbe\xb6\x7f\x01\x10\x9b\xd0\xe3\n\xf2\xf6\xde4h\x17\x00#?9]5Q\xf3\xb6dlvؙr\x0e@\xe8ѿ\xbb\xfbp\xff\xff\xf5\x93e\x00\x8b\xb1a\xea%\xb3<_\"P\x04\x03\x13\x12x\xd8!#\xdcg>!J`\x8c#\xe8Ǥ\x00\x13\xfeX?.\xf6\x1czd\xa1\xa9\xf8\xf2\x9c\xf4\xd7\xc9\xea\x19\xae\x1b\x85^\xa2\xc0jca\x04\xd9\xe1T>ڱZ\b-Ȏ\"0\xf6\x8c\x11\xbd\x1c\x85<>\xa1\x05\xe3!l\xfe\xc0FjX#k\x1a\xd5&9\xab\xfd8 \v06a\xeb\xe9\xaf\xc7\xdc\x11$\xe4C\x9d\x11\x1c5?>\xe4\x05\xd9\x1b\a\x83q\t_\x83\xf1\x16:s\x00F=\x05\x92?ɗCb\r\x9f\x02#\x90o\xc3\nv\"}\\-\x97[\x92\xc9WM\xe8\xba\xe4I\x0e\xcbl\x11\xda$\t\x1c\x97\x16\at\xcbH\xdb\xcap\xb3#\xc1F\x12\xe3\xd2\xf4Te\xe8\xbe\xf8\xa0\xb3\xafxtb\xbcy\x82U\x0e\xdaEQ\x98\xfc\xf6\xe4E6\xc2w\x14P\x0f\x94F([K\x15G\xa2uI\xd9\xf9\xf2\xcb\xfa+LGg1\xce\xd9ϼ\x1f7ƣ\x04J\x18\xf9\x16\xb9\x88\xd8r\xe8rN\xf4\xb6\x0f\xe4%\xffi\x1c\xa1?\xa7?\xa6MG\xa2\xba\xff\x990\x8ajU\xc3m\x1e6\xb0AH\xbd5\x82\xb6\x86\x0f\x1enM\x87\xee\xd6D\xfc\xcf\x05P\xa6c\xa5\xc4>O\x82\xd39y\x1e\\X;5\xd88ޮ\xe85\xef\xe4u\x8f\xcd\x13\x03i\x16jitv\x1b\xf8\x8cW3\xf9|>_\xfd$|\xde\xe0P\x86|K\xdb\xf3U\x00cm\xbe\"\x8c\xbb\xbb\xba\xf7;\x84\xcd\xd4}\x9bO\xd2Fm\x03+\xa2\x81,r5\xd59\"I<\x16L\xe8l\xac/R^\xe1<\x97\xc2hUc\xe3.\x81>E\xf2\x18\x98\xef8C\xbeP~L\x90[\x8f\xbbq\xc6zAo\xf3P\xbf@\x13r\x0fG\xb4\xf0@\xb2+\xe6p\xa7\x97\xd4\xf3T\xd0g\x8f\x87\xb9\xe53\xec_w\xa8\x91e\x9c\"Dl\x18EqDtj^uf\r\xf0)\xc5l/3\x9b\x11tD\x90\x9dv\xef\xf1pI4\xfcH\xdc\xf1\xbe\xff1\xe4\x1b\xbd\x17'\xc0\x8c-2z\x99\xb5\xb8~b\xb0G\xc1\xecr\x1b\x9a\xa8\x06o\xb0\x97\xb8\f\x03\xf2@\xf8\xb0|\b\xbc'\xbf\xad\x94\xf0\xaa4B\\\xe6\xef\x86\xe5\xab\xfcs\xa5䯟\xdf\x7f^\xc1;k!\xc8\x0eYUk\x93\x9b\x1a\xed\xe4\xb6{\x9d'\xeekHd\x7f\xbe\xf9'\xbc\x84\xbe8\xe7\x19ܬs\xf7\x1f\xf4\xe6Π\x94\xa2uQ%0\xe8\xdcT\xb1\xbbQ\xcd2\x1f\xe6\x1aq´\t\xc1\xa1\xb9l=\x9d\xbe\xc4h/!Uz\xc2Kl\x06\xf0\xad:\nUu\xa6\xafJ\xb4\x91\xd0Qs\x16=\xf9\xfc\a\x96\xbc\x1b\xc3t<(\aӶ\xa9m\xcaWL\xfe\xa61[\xbc6\x16f\x14\x99/\xbcz<\xe0Y\x03]\x8c\xa4\xf8\U000917b7\x8d\x91\x9bq\xac7\x89\xb5\xfdǜ3\x9f?\xff\xceX\xefw&\xcex\xf3\x19\xa8\xeft\xe7$\x83\xa3\x16\x9bC\xe3\xb0$\x84\xd0\xce\xf4ދ \xeb\x83>us\x8d\xf8n0\xe4\xcc\xc6\xe1̻߽\xb9\xfa\xf6\xaa\xf8\xb3z^,F\xfdƱ+\x10N%\xf7\xd8e\xe3\xca\xdf\x01\x00\x00\xff\xff\xec\xa0\xe0\xa1k\r\x00\x00"), } diff --git a/pkg/apis/velero/v1/schedule_types.go b/pkg/apis/velero/v1/schedule_types.go index 6cb553b9a8..6a5f885abc 100644 --- a/pkg/apis/velero/v1/schedule_types.go +++ b/pkg/apis/velero/v1/schedule_types.go @@ -42,6 +42,13 @@ type ScheduleSpec struct { // Paused specifies whether the schedule is paused or not // +optional Paused bool `json:"paused,omitempty"` + + // SkipImmediately specifies whether to skip backup if schedule is due immediately from `schedule.status.lastBackup` timestamp when schedule is unpaused or if schedule is new. + // If true, backup will be skipped immediately when schedule is unpaused if it is due based on .Status.LastBackupTimestamp or schedule is new, and will run at next schedule time. + // If false, backup will not be skipped immediately when schedule is unpaused, but will run at next schedule time. + // If empty, will follow server configuration (default: false). + // +optional + SkipImmediately *bool `json:"skipImmediately,omitempty"` } // SchedulePhase is a string representation of the lifecycle phase @@ -75,6 +82,11 @@ type ScheduleStatus struct { // +nullable LastBackup *metav1.Time `json:"lastBackup,omitempty"` + // LastSkipped is the last time a Schedule was skipped + // +optional + // +nullable + LastSkipped *metav1.Time `json:"lastSkipped,omitempty"` + // ValidationErrors is a slice of all validation errors (if // applicable) // +optional diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index 7688fc0252..68a46f8fe1 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -1511,6 +1511,10 @@ func (in *ScheduleStatus) DeepCopyInto(out *ScheduleStatus) { in, out := &in.LastBackup, &out.LastBackup *out = (*in).DeepCopy() } + if in.LastSkipped != nil { + in, out := &in.LastSkipped, &out.LastSkipped + *out = (*in).DeepCopy() + } if in.ValidationErrors != nil { in, out := &in.ValidationErrors, &out.ValidationErrors *out = make([]string, len(*in)) diff --git a/pkg/builder/schedule_builder.go b/pkg/builder/schedule_builder.go index 136487ec54..7c9a9b1d94 100644 --- a/pkg/builder/schedule_builder.go +++ b/pkg/builder/schedule_builder.go @@ -89,3 +89,9 @@ func (b *ScheduleBuilder) Template(spec velerov1api.BackupSpec) *ScheduleBuilder b.object.Spec.Template = spec return b } + +// SkipImmediately sets the Schedule's SkipImmediately. +func (b *ScheduleBuilder) SkipImmediately(skip *bool) *ScheduleBuilder { + b.object.Spec.SkipImmediately = skip + return b +} diff --git a/pkg/cmd/cli/schedule/create.go b/pkg/cmd/cli/schedule/create.go index 85f53acc32..eb66078713 100644 --- a/pkg/cmd/cli/schedule/create.go +++ b/pkg/cmd/cli/schedule/create.go @@ -82,6 +82,7 @@ example: "@every 2h30m".`, type CreateOptions struct { BackupOptions *backup.CreateOptions + SkipOptions *SkipOptions Schedule string UseOwnerReferencesInBackup bool Paused bool @@ -95,6 +96,7 @@ func NewCreateOptions() *CreateOptions { func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) { o.BackupOptions.BindFlags(flags) + o.SkipOptions.BindFlags(flags) flags.StringVar(&o.Schedule, "schedule", o.Schedule, "A cron expression specifying a recurring schedule for this backup to run") flags.BoolVar(&o.UseOwnerReferencesInBackup, "use-owner-references-in-backup", o.UseOwnerReferencesInBackup, "Specifies whether to use OwnerReferences on backups created by this Schedule. Notice: if set to true, when schedule is deleted, backups will be deleted too.") flags.BoolVar(&o.Paused, "paused", o.Paused, "Specifies whether the newly created schedule is paused or not.") @@ -160,6 +162,7 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error { Schedule: o.Schedule, UseOwnerReferencesInBackup: &o.UseOwnerReferencesInBackup, Paused: o.Paused, + SkipImmediately: o.SkipOptions.SkipImmediately.Value, }, } diff --git a/pkg/cmd/cli/schedule/pause.go b/pkg/cmd/cli/schedule/pause.go index decda82605..820e887a7d 100644 --- a/pkg/cmd/cli/schedule/pause.go +++ b/pkg/cmd/cli/schedule/pause.go @@ -22,6 +22,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/pflag" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" kubeerrs "k8s.io/apimachinery/pkg/util/errors" @@ -36,6 +37,7 @@ import ( // NewPauseCommand creates the command for pause func NewPauseCommand(f client.Factory, use string) *cobra.Command { o := cli.NewSelectOptions("pause", "schedule") + pauseOpts := NewPauseOptions() c := &cobra.Command{ Use: use, @@ -54,16 +56,31 @@ func NewPauseCommand(f client.Factory, use string) *cobra.Command { Run: func(c *cobra.Command, args []string) { cmd.CheckError(o.Complete(args)) cmd.CheckError(o.Validate()) - cmd.CheckError(runPause(f, o, true)) + cmd.CheckError(runPause(f, o, true, pauseOpts.SkipOptions.SkipImmediately.Value)) }, } o.BindFlags(c.Flags()) + pauseOpts.BindFlags(c.Flags()) return c } -func runPause(f client.Factory, o *cli.SelectOptions, paused bool) error { +type PauseOptions struct { + SkipOptions *SkipOptions +} + +func NewPauseOptions() *PauseOptions { + return &PauseOptions{ + SkipOptions: NewSkipOptions(), + } +} + +func (o *PauseOptions) BindFlags(flags *pflag.FlagSet) { + o.SkipOptions.BindFlags(flags) +} + +func runPause(f client.Factory, o *cli.SelectOptions, paused bool, skipImmediately *bool) error { crClient, err := f.KubebuilderClient() if err != nil { return err @@ -120,6 +137,7 @@ func runPause(f client.Factory, o *cli.SelectOptions, paused bool) error { continue } schedule.Spec.Paused = paused + schedule.Spec.SkipImmediately = skipImmediately if err := crClient.Update(context.TODO(), schedule); err != nil { return errors.Wrapf(err, "failed to update schedule %s", schedule.Name) } diff --git a/pkg/cmd/cli/schedule/skip_options.go b/pkg/cmd/cli/schedule/skip_options.go new file mode 100644 index 0000000000..e102b23f4e --- /dev/null +++ b/pkg/cmd/cli/schedule/skip_options.go @@ -0,0 +1,19 @@ +package schedule + +import ( + "github.com/spf13/pflag" + "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" +) + +type SkipOptions struct { + SkipImmediately flag.OptionalBool +} + +func NewSkipOptions() *SkipOptions { + return &SkipOptions{} +} + +func (o *SkipOptions) BindFlags(flags *pflag.FlagSet) { + f := flags.VarPF(&o.SkipImmediately, "skip-immediately", "", "Skip the next scheduled backup immediately") + f.NoOptDefVal = "" // default to nil so server options can take precedence +} \ No newline at end of file diff --git a/pkg/cmd/cli/schedule/unpause.go b/pkg/cmd/cli/schedule/unpause.go index 6b9ff2a0a7..72197a9349 100644 --- a/pkg/cmd/cli/schedule/unpause.go +++ b/pkg/cmd/cli/schedule/unpause.go @@ -27,7 +27,7 @@ import ( // NewUnpauseCommand creates the command for unpause func NewUnpauseCommand(f client.Factory, use string) *cobra.Command { o := cli.NewSelectOptions("pause", "schedule") - + pauseOpts := NewPauseOptions() c := &cobra.Command{ Use: use, Short: "Unpause schedules", @@ -45,11 +45,12 @@ func NewUnpauseCommand(f client.Factory, use string) *cobra.Command { Run: func(c *cobra.Command, args []string) { cmd.CheckError(o.Complete(args)) cmd.CheckError(o.Validate()) - cmd.CheckError(runPause(f, o, false)) + cmd.CheckError(runPause(f, o, false, pauseOpts.SkipOptions.SkipImmediately.Value)) }, } o.BindFlags(c.Flags()) + pauseOpts.BindFlags(c.Flags()) return c } diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 5098a7b13f..cf42adea23 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -133,6 +133,7 @@ type serverConfig struct { maxConcurrentK8SConnections int defaultSnapshotMoveData bool disableInformerCache bool + scheduleSkipImmediately bool } func NewCommand(f client.Factory) *cobra.Command { @@ -163,6 +164,7 @@ func NewCommand(f client.Factory) *cobra.Command { maxConcurrentK8SConnections: defaultMaxConcurrentK8SConnections, defaultSnapshotMoveData: false, disableInformerCache: defaultDisableInformerCache, + scheduleSkipImmediately: false, } ) @@ -235,6 +237,7 @@ func NewCommand(f client.Factory) *cobra.Command { command.Flags().IntVar(&config.maxConcurrentK8SConnections, "max-concurrent-k8s-connections", config.maxConcurrentK8SConnections, "Max concurrent connections number that Velero can create with kube-apiserver. Default is 30.") command.Flags().BoolVar(&config.defaultSnapshotMoveData, "default-snapshot-move-data", config.defaultSnapshotMoveData, "Move data by default for all snapshots supporting data movement.") command.Flags().BoolVar(&config.disableInformerCache, "disable-informer-cache", config.disableInformerCache, "Disable informer cache for Get calls on restore. With this enabled, it will speed up restore in cases where there are backup resources which already exist in the cluster, but for very large clusters this will increase velero memory usage. Default is false (don't disable).") + command.Flags().BoolVar(&config.scheduleSkipImmediately, "schedule-skip-immediately", config.scheduleSkipImmediately, "Skip the first scheduled backup immediately after creating a schedule. Default is false (don't skip).") return command } @@ -915,7 +918,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string } if _, ok := enabledRuntimeControllers[controller.Schedule]; ok { - if err := controller.NewScheduleReconciler(s.namespace, s.logger, s.mgr.GetClient(), s.metrics).SetupWithManager(s.mgr); err != nil { + if err := controller.NewScheduleReconciler(s.namespace, s.logger, s.mgr.GetClient(), s.metrics, s.config.scheduleSkipImmediately).SetupWithManager(s.mgr); err != nil { s.logger.Fatal(err, "unable to create controller", "controller", controller.Schedule) } } diff --git a/pkg/controller/schedule_controller.go b/pkg/controller/schedule_controller.go index ed36896eee..120b33b5b1 100644 --- a/pkg/controller/schedule_controller.go +++ b/pkg/controller/schedule_controller.go @@ -48,6 +48,7 @@ type scheduleReconciler struct { logger logrus.FieldLogger clock clocks.WithTickerAndDelayedExecution metrics *metrics.ServerMetrics + skipImmediately bool } func NewScheduleReconciler( @@ -55,6 +56,7 @@ func NewScheduleReconciler( logger logrus.FieldLogger, client client.Client, metrics *metrics.ServerMetrics, + skipImmediately bool, ) *scheduleReconciler { return &scheduleReconciler{ Client: client, @@ -62,6 +64,7 @@ func NewScheduleReconciler( logger: logger, clock: clocks.RealClock{}, metrics: metrics, + skipImmediately: skipImmediately, } } @@ -99,11 +102,18 @@ func (c *scheduleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } return ctrl.Result{}, errors.Wrapf(err, "error getting schedule %s", req.String()) } - c.metrics.InitSchedule(schedule.Name) original := schedule.DeepCopy() + if schedule.Spec.SkipImmediately == nil { + schedule.Spec.SkipImmediately = &c.skipImmediately + } + if schedule.Spec.SkipImmediately != nil && *schedule.Spec.SkipImmediately { + *schedule.Spec.SkipImmediately = false + schedule.Status.LastSkipped = &metav1.Time{Time: c.clock.Now()} + } + // validation - even if the item is Enabled, we can't trust it // so re-validate currentPhase := schedule.Status.Phase @@ -117,7 +127,9 @@ func (c *scheduleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c } // update status if it's changed - if currentPhase != schedule.Status.Phase { + if currentPhase != schedule.Status.Phase || + original.Status.LastSkipped != schedule.Status.LastSkipped || + original.Status.LastBackup != schedule.Status.LastBackup { if err := c.Patch(ctx, schedule, client.MergeFrom(original)); err != nil { return ctrl.Result{}, errors.Wrapf(err, "error updating phase of schedule %s to %s", req.String(), schedule.Status.Phase) } @@ -132,13 +144,15 @@ func (c *scheduleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c // If there are backup created by this schedule still in New or InProgress state, // skip current backup creation to avoid running overlap backups. // As the schedule must be validated before checking whether it's due, we cannot put the checking log in Predicate - if c.ifDue(schedule, cronSchedule) && !c.checkIfBackupInNewOrProgress(schedule) { + due, nextRunTime := c.ifDue(schedule, cronSchedule) + durationTillNextRun := nextRunTime.Sub(c.clock.Now()) + if due && !c.checkIfBackupInNewOrProgress(schedule) { if err := c.submitBackup(ctx, schedule); err != nil { - return ctrl.Result{}, errors.Wrapf(err, "error submit backup for schedule %s", req.String()) + return ctrl.Result{RequeueAfter: durationTillNextRun}, errors.Wrapf(err, "error submit backup for schedule %s", req.String()) } } - return ctrl.Result{}, nil + return ctrl.Result{RequeueAfter: durationTillNextRun}, nil } func parseCronSchedule(itm *velerov1.Schedule, logger logrus.FieldLogger) (cron.Schedule, []string) { @@ -208,16 +222,16 @@ func (c *scheduleReconciler) checkIfBackupInNewOrProgress(schedule *velerov1.Sch } // ifDue check whether schedule is due to create a new backup. -func (c *scheduleReconciler) ifDue(schedule *velerov1.Schedule, cronSchedule cron.Schedule) bool { +func (c *scheduleReconciler) ifDue(schedule *velerov1.Schedule, cronSchedule cron.Schedule) (bool, time.Time) { isDue, nextRunTime := getNextRunTime(schedule, cronSchedule, c.clock.Now()) log := c.logger.WithField("schedule", kube.NamespaceAndName(schedule)) if !isDue { log.WithField("nextRunTime", nextRunTime).Debug("Schedule is not due, skipping") - return false + return false, nextRunTime } - return true + return true, nextRunTime } // submitBackup create a backup from schedule. @@ -249,6 +263,9 @@ func getNextRunTime(schedule *velerov1.Schedule, cronSchedule cron.Schedule, asO } else { lastBackupTime = schedule.CreationTimestamp.Time } + if schedule.Status.LastSkipped != nil && schedule.Status.LastSkipped.After(lastBackupTime) { + lastBackupTime = schedule.Status.LastSkipped.Time + } nextRunTime := cronSchedule.Next(lastBackupTime) diff --git a/pkg/controller/schedule_controller_test.go b/pkg/controller/schedule_controller_test.go index 44e728d778..6e1c15254c 100644 --- a/pkg/controller/schedule_controller_test.go +++ b/pkg/controller/schedule_controller_test.go @@ -27,6 +27,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes/scheme" testclocks "k8s.io/utils/clock/testing" + "k8s.io/utils/pointer" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -36,6 +37,7 @@ import ( velerotest "github.com/vmware-tanzu/velero/pkg/test" ) +// Test reconcile function of schedule controller. Pause is not covered as event filter will not allow it through func TestReconcileOfSchedule(t *testing.T) { require.Nil(t, velerov1.AddToScheme(scheme.Scheme)) @@ -52,6 +54,7 @@ func TestReconcileOfSchedule(t *testing.T) { expectedValidationErrors []string expectedBackupCreate *velerov1.Backup expectedLastBackup string + expectedLastSkipped string backup *velerov1.Backup }{ { @@ -88,6 +91,13 @@ func TestReconcileOfSchedule(t *testing.T) { expectedBackupCreate: builder.ForBackup("ns", "name-20170101120000").ObjectMeta(builder.WithLabels(velerov1.ScheduleNameLabel, "name")).Result(), expectedLastBackup: "2017-01-01 12:00:00", }, + { + name: "schedule with phase New and SkipImmediately gets validated and does not trigger a backup", + schedule: newScheduleBuilder(velerov1.SchedulePhaseNew).CronSchedule("@every 5m").SkipImmediately(pointer.Bool(true)).Result(), + fakeClockTime: "2017-01-01 12:00:00", + expectedPhase: string(velerov1.SchedulePhaseEnabled), + expectedLastSkipped: "2017-01-01 12:00:00", + }, { name: "schedule with phase Enabled gets re-validated and triggers a backup if valid", schedule: newScheduleBuilder(velerov1.SchedulePhaseEnabled).CronSchedule("@every 5m").Result(), @@ -103,6 +113,13 @@ func TestReconcileOfSchedule(t *testing.T) { expectedBackupCreate: builder.ForBackup("ns", "name-20170101120000").ObjectMeta(builder.WithLabels(velerov1.ScheduleNameLabel, "name")).Result(), expectedLastBackup: "2017-01-01 12:00:00", }, + { + name: "schedule that's already run but has SkippedImmediately do not get LastBackup updated", + schedule: newScheduleBuilder(velerov1.SchedulePhaseEnabled).CronSchedule("@every 5m").LastBackupTime("2000-01-01 00:00:00").SkipImmediately(pointer.Bool(true)).Result(), + fakeClockTime: "2017-01-01 12:00:00", + expectedLastBackup: "2000-01-01 00:00:00", + expectedLastSkipped: "2017-01-01 12:00:00", + }, { name: "schedule already has backup in New state.", schedule: newScheduleBuilder(velerov1.SchedulePhaseEnabled).CronSchedule("@every 5m").LastBackupTime("2000-01-01 00:00:00").Result(), @@ -119,8 +136,8 @@ func TestReconcileOfSchedule(t *testing.T) { testTime time.Time err error ) - - reconciler := NewScheduleReconciler("namespace", logger, client, metrics.NewServerMetrics()) + name := t.Name() + reconciler := NewScheduleReconciler("namespace"+name, logger, client, metrics.NewServerMetrics(), false) if test.fakeClockTime != "" { testTime, err = time.Parse("2006-01-02 15:04:05", test.fakeClockTime) @@ -151,8 +168,14 @@ func TestReconcileOfSchedule(t *testing.T) { } if len(test.expectedLastBackup) > 0 { require.Nil(t, err) + require.NotNil(t, schedule.Status.LastBackup) assert.Equal(t, parseTime(test.expectedLastBackup).Unix(), schedule.Status.LastBackup.Unix()) } + if len(test.expectedLastSkipped) > 0 { + require.Nil(t, err) + require.NotNil(t, schedule.Status.LastSkipped) + assert.Equal(t, parseTime(test.expectedLastSkipped).Unix(), schedule.Status.LastSkipped.Unix()) + } backups := &velerov1.BackupList{} require.Nil(t, client.List(ctx, backups)) @@ -403,7 +426,7 @@ func TestCheckIfBackupInNewOrProgress(t *testing.T) { err = client.Create(ctx, newBackup) require.NoError(t, err, "fail to create backup in New phase in TestCheckIfBackupInNewOrProgress: %v", err) - reconciler := NewScheduleReconciler("ns", logger, client, metrics.NewServerMetrics()) + reconciler := NewScheduleReconciler("ns", logger, client, metrics.NewServerMetrics(), false) result := reconciler.checkIfBackupInNewOrProgress(testSchedule) assert.True(t, result) @@ -418,7 +441,7 @@ func TestCheckIfBackupInNewOrProgress(t *testing.T) { err = client.Create(ctx, inProgressBackup) require.NoError(t, err, "fail to create backup in InProgress phase in TestCheckIfBackupInNewOrProgress: %v", err) - reconciler = NewScheduleReconciler("namespace", logger, client, metrics.NewServerMetrics()) + reconciler = NewScheduleReconciler("namespace", logger, client, metrics.NewServerMetrics(), false) result = reconciler.checkIfBackupInNewOrProgress(testSchedule) assert.True(t, result) }