async def test_prov(self): s_provenance.reset() async with self.getTestCore() as real, real.getLocalProxy() as core: # Non-existent iden self.none(await core.getProvStack('abcd')) await core.addTrigger('node:add', '[ test:int=1 ]', info={'form': 'test:str'}) await s_common.aspin(core.eval('[ test:str=foo ]')) await self.agenlen(1, core.eval('test:int')) await self.agenlen(0, core.eval('test:int | delnode')) splices = await alist(core.splices(0, 1000)) self.len(9, splices) idens = [splice[1]['prov'] for splice in splices] self.eq(idens[0], idens[1]) self.eq(idens[0], idens[2]) self.eq(idens[3], idens[4]) self.eq(idens[7], idens[8]) # node:add and prop:set self.eq(idens[5], idens[6]) # The source splices prov1 = await core.getProvStack(idens[0]) self.eq(({}, ()), prov1) # The test:str splices prov2 = await core.getProvStack(idens[3]) rootiden = prov2[1][0][1]['user'] s2 = ('storm', {'q': '[ test:str=foo ]', 'user': rootiden}) self.eq((s2, ), prov2[1]) # Validate that the iden calc itself is correct rawprov = ({}, [('storm', (('q', '[ test:str=foo ]'), ('user', rootiden)))]) hash = hashlib.md5(s_msgpack.en(rawprov)).hexdigest() self.eq(hash, idens[3]) # The trigger splices prov3 = await core.getProvStack(idens[5]) s3 = ('trig', {'cond': 'node:add', 'form': 'test:str', 'tag': None, 'prop': None}) s4 = ('storm', {'q': '[ test:int=1 ]', 'user': rootiden}) self.eq((s2, s3, s4), prov3[1]) # prop:del/node:del prov4 = await core.getProvStack(idens[7]) ds2 = ('storm', {'q': 'test:int | delnode', 'user': rootiden}) ds3 = ('stormcmd', {'name': 'delnode', 'argv': ()}) self.eq((ds2, ds3), prov4[1]) # Test the streaming API provstacks = await alist(core.provStacks(0, 1000)) correct = [(idens[0], prov1), (idens[3], prov2), (idens[5], prov3), (idens[7], prov4)] self.eq(provstacks, correct)
async def test_cron(self): MONO_DELT = 1543827303.0 unixtime = datetime.datetime(year=2018, month=12, day=5, hour=7, minute=0, tzinfo=tz.utc).timestamp() sync = asyncio.Event() lastquery = None s_provenance.reset() def timetime(): return unixtime def looptime(): return unixtime - MONO_DELT async def myeval(query, user=None): nonlocal lastquery lastquery = query sync.set() return yield None loop = asyncio.get_running_loop() with mock.patch.object(loop, 'time', looptime), mock.patch('time.time', timetime): async with self.getTestCoreAndProxy() as (realcore, core): outp = self.getTestOutp() async with await s_cmdr.getItemCmdr(core, outp=outp) as cmdr: # Various silliness await cmdr.runCmdLine('cron') self.true(outp.expect('Manages cron jobs in a cortex')) await cmdr.runCmdLine('cron timemachine') self.true(outp.expect('invalid choice')) await cmdr.runCmdLine('cron list') self.true(outp.expect('No cron jobs found')) await cmdr.runCmdLine('cron ls') self.true(outp.expect('No cron jobs found')) outp.clear() await cmdr.runCmdLine( "cron add -M+1,beeroclock {[graph:node='*' :type=m1]}") self.true(outp.expect('failed to parse parameter')) await cmdr.runCmdLine( "cron add -m nosuchmonth -d=-2 {#foo}") self.true(outp.expect('failed to parse fixed parameter')) outp.clear() await cmdr.runCmdLine( "cron add -m 8nosuchmonth -d=-2 {#foo}") self.true(outp.expect('failed to parse fixed parameter')) await cmdr.runCmdLine("cron add -d=, {#foo}") self.true(outp.expect('failed to parse day value')) await cmdr.runCmdLine("cron add -dMon -m +3 {#foo}") self.true( outp.expect( 'provide a recurrence value with day of week')) await cmdr.runCmdLine("cron add -dMon -m June {#foo}") self.true( outp.expect('fix month or year with day of week')) await cmdr.runCmdLine("cron add -dMon -m +3 -y +2 {#foo}") self.true(outp.expect('more than 1 recurrence')) await cmdr.runCmdLine("cron add --year=2019 {#foo}") self.true(outp.expect('year may not be a fixed value')) await cmdr.runCmdLine("cron add {#foo}") self.true( outp.expect('must provide at least one optional')) await cmdr.runCmdLine("cron add -H3 -M +4 {#foo}") self.true(outp.expect('fixed unit may not be larger')) outp.clear() await cmdr.runCmdLine('cron add -d Tuesday,1 {#foo}') self.true(outp.expect('failed to parse day value')) outp.clear() await cmdr.runCmdLine('cron add -d Fri,3 {#foo}') self.true(outp.expect('failed to parse day value')) outp.clear() await cmdr.runCmdLine('cron add }') self.true(outp.expect('BadSyntax')) ################## oldsplices = len(await alist(core.splices(0, 1000))) # Start simple: add a cron job that creates a node every minute outp.clear() await cmdr.runCmdLine( "cron add -M +1 {[graph:node='*' :type=m1]}") self.true(outp.expect('Created cron job')) guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime += 60 await cmdr.runCmdLine('cron list') self.true(outp.expect(':type=m1')) # Make sure it ran await self.agenlen(1, core.eval('graph:node:type=m1')) # Make sure the provenance of the new splices looks right splices = await alist(core.splices(oldsplices, 1000)) self.gt(len(splices), 1) aliases = [splice[1]['prov'] for splice in splices] self.true(all(a == aliases[0] for a in aliases)) prov = await core.getProvStack(aliases[0]) rootiden = prov[1][1][1]['user'] correct = ({}, (('cron', { 'iden': guid }), ('storm', { 'q': "[graph:node='*' :type=m1]", 'user': rootiden }))) self.eq(prov, correct) await cmdr.runCmdLine( f"cron mod {guid[:6]} {{[graph:node='*' :type=m2]}}") self.true(outp.expect('Modified cron job')) await cmdr.runCmdLine( f"cron edit xxx {{[graph:node='*' :type=m2]}}") self.true(outp.expect('does not match')) await cmdr.runCmdLine(f"cron mod xxx yyy") self.true( outp.expect( 'expected second argument to start with {')) # Make sure the old one didn't run and the new query ran unixtime += 60 await self.agenlen(1, core.eval('graph:node:type=m1')) await self.agenlen(1, core.eval('graph:node:type=m2')) outp.clear() # Delete the job await cmdr.runCmdLine(f"cron del {guid}") self.true(outp.expect('Deleted cron job')) await cmdr.runCmdLine(f"cron del xxx") self.true(outp.expect('does not match')) await cmdr.runCmdLine(f"cron rm xxx") self.true(outp.expect('does not match')) # Make sure deleted job didn't run unixtime += 60 await self.agenlen(1, core.eval('graph:node:type=m1')) await self.agenlen(1, core.eval('graph:node:type=m2')) # Test fixed minute, i.e. every hour at 17 past unixtime = datetime.datetime(year=2018, month=12, day=5, hour=7, minute=10, tzinfo=tz.utc).timestamp() await cmdr.runCmdLine( "cron add -M 17 {[graph:node='*' :type=m3]}") guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime += 7 * MINSECS # Make sure it runs. We add the cron list to give the cron scheduler a chance to run await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=m3')) await cmdr.runCmdLine(f"cron del {guid}") ################## # Test day increment await cmdr.runCmdLine( "cron add -d +2 {[graph:node='*' :type=d1]}") self.true(outp.expect('Created cron job')) guid1 = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime += DAYSECS # Make sure it *didn't* run await self.agenlen(0, core.eval('graph:node:type=d1')) unixtime += DAYSECS # Make sure it runs. We add the cron list to give the cron scheduler a chance to run await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=d1')) unixtime += DAYSECS * 2 await cmdr.runCmdLine('cron list') await self.agenlen(2, core.eval('graph:node:type=d1')) ################## # Test fixed day of week: every Monday and Thursday at 3am unixtime = datetime.datetime( year=2018, month=12, day=11, hour=7, minute=10, tzinfo=tz.utc).timestamp() # A Tuesday await cmdr.runCmdLine( "cron add -H 3 -d Mon,Thursday {[graph:node='*' :type=d2]}" ) guid2 = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime = datetime.datetime( year=2018, month=12, day=13, hour=3, minute=10, tzinfo=tz.utc).timestamp() # Now Thursday await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=d2')) await cmdr.runCmdLine(f"cron del {guid1}") await cmdr.runCmdLine(f"cron del {guid2}") await cmdr.runCmdLine( "cron add -H 3 -d Noday {[graph:node='*' :type=d2]}") self.true(outp.expect('failed to parse day value "Noday"')) ################## # Test fixed day of month: second-to-last day of month await cmdr.runCmdLine( "cron add -d-2 -mDec {[graph:node='*' :type=d3]}") guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime = datetime.datetime( year=2018, month=12, day=29, hour=0, minute=0, tzinfo=tz.utc).timestamp() # Now Thursday await cmdr.runCmdLine('cron list') await self.agenlen(0, core.eval('graph:node:type=d3') ) # Not yet unixtime += DAYSECS await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=d3')) await cmdr.runCmdLine(f"cron del {guid}") ################## # Test month increment await cmdr.runCmdLine( "cron add -m +2 -d=4 {[graph:node='*' :type=month1]}") unixtime = datetime.datetime( year=2019, month=2, day=4, hour=0, minute=0, tzinfo=tz.utc).timestamp() # Now Thursday await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=month1')) ################## # Test year increment await cmdr.runCmdLine( "cron add -y +2 {[graph:node='*' :type=year1]}") guid2 = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime = datetime.datetime( year=2021, month=1, day=1, hour=0, minute=0, tzinfo=tz.utc).timestamp() # Now Thursday await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=year1')) # Make sure second-to-last day works for February await cmdr.runCmdLine( "cron add -m February -d=-2 {[graph:node='*' :type=year2]}" ) unixtime = datetime.datetime( year=2021, month=2, day=27, hour=0, minute=0, tzinfo=tz.utc).timestamp() # Now Thursday await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=year2')) ################## # Test 'at' command outp.clear() await cmdr.runCmdLine('at') self.true(outp.expect('Adds a non-recurring')) await cmdr.runCmdLine('at --not-a-real-flag') self.true(outp.expect('the following arguments')) await cmdr.runCmdLine('at {#foo} {#bar}') self.true(outp.expect('only a single query')) await cmdr.runCmdLine('at {#foo}') self.true(outp.expect('at least')) await cmdr.runCmdLine('at +1') self.true(outp.expect('missing unit')) await cmdr.runCmdLine('at +1parsec') self.true(outp.expect('Trouble parsing')) await cmdr.runCmdLine('at +1day') self.true(outp.expect('Missing query')) await cmdr.runCmdLine( "at +5 minutes {[graph:node='*' :type=at1]}") unixtime += 5 * MINSECS await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=at1')) await cmdr.runCmdLine( "at +1 day +7 days {[graph:node='*' :type=at2]}") guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime += DAYSECS await self.agenlen(1, core.eval('graph:node:type=at2')) unixtime += 6 * DAYSECS + 1 await cmdr.runCmdLine('cron list') await self.agenlen(2, core.eval('graph:node:type=at2')) await cmdr.runCmdLine( "at 202104170415 {[graph:node='*' :type=at3]}") unixtime = datetime.datetime( year=2021, month=4, day=17, hour=4, minute=15, tzinfo=tz.utc).timestamp() # Now Thursday await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=at3')) outp.clear() ################## # Test 'stat' command await cmdr.runCmdLine(f'cron stat xxx') self.true(outp.expect('provided iden does not match any')) await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true( outp.expect( 'last result: finished successfully with 1 nodes' )) self.true(outp.expect('entries: <None>')) await cmdr.runCmdLine(f'cron stat {guid2[:6]}') self.true( outp.expect( "{'month': 1, 'hour': 0, 'minute': 0, 'dayofmonth': 1}" )) outp.clear() ################## # Test 'enable' 'disable' commands await cmdr.runCmdLine(f'cron enable xxx') self.true(outp.expect('provided iden does not match any')) outp.clear() await cmdr.runCmdLine(f'cron disable xxx') self.true(outp.expect('provided iden does not match any')) outp.clear() await cmdr.runCmdLine(f'cron disable {guid[:6]}') await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect(f'enabled: N')) outp.clear() await cmdr.runCmdLine(f'cron enable {guid[:6]}') await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect(f'enabled: Y')) outp.clear() ################### # Delete an expired at job outp.clear() await cmdr.runCmdLine(f"cron del {guid}") self.true(outp.expect('Deleted cron job')) ################## # Test the aliases outp.clear() await cmdr.runCmdLine('cron add --hourly 15 {#bar}') guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect("{'minute': 15}")) outp.clear() await cmdr.runCmdLine('cron add --daily 05:47 {#bar}') guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect("{'hour': 5, 'minute': 47")) outp.clear() await cmdr.runCmdLine('cron add --monthly=-1:12:30 {#bar}') guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true( outp.expect( "{'hour': 12, 'minute': 30, 'dayofmonth': -1}")) outp.clear() await cmdr.runCmdLine( 'cron add --yearly 04:17:12:30 {#bar}') guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true( outp.expect( "{'month': 4, 'hour': 12, 'minute': 30, 'dayofmonth': 17}" )) outp.clear() await cmdr.runCmdLine('cron add --yearly 04:17:12 {#bar}') self.true(outp.expect('Failed to parse parameter')) outp.clear() await cmdr.runCmdLine('cron add --daily xx:xx {#bar}') self.true(outp.expect('Failed to parse ..ly parameter')) outp.clear() await cmdr.runCmdLine('cron add --hourly 1 -M 17 {#bar}') self.true(outp.expect('may not use both'))
async def test_prov(self): s_provenance.reset() async with self.getTestCore() as real, real.getLocalProxy() as core: # Non-existent iden self.none(await core.getProvStack('abcd')) await real.view.addTrigger({ 'cond': 'node:add', 'form': 'test:str', 'storm': '[ test:int=1 ]', }) await s_common.aspin(core.eval('[ test:str=foo ]')) await self.agenlen(1, core.eval('test:int')) await self.agenlen(0, core.eval('test:int | delnode')) splices = await alist(core.splices(None, 1000)) self.len(9, splices) idens = [splice[1][1].get('prov') for splice in splices] # source splices self.eq(idens[0], idens[1]) self.eq(idens[0], idens[2]) # test:str add self.eq(idens[3], idens[4]) # trigger self.eq(idens[5], idens[6]) # test:int delnode self.eq(idens[7], idens[8]) provs = [await core.getProvStack(iden) for iden in idens] # The meta:source splices self.eq(({}, (('init', {'meth': '_initCoreMods'}),)), provs[0]) # The test:str splices prov = provs[3][1] rootiden = prov[0][1]['user'] s2 = ('storm', {'q': '[ test:str=foo ]', 'user': rootiden}) self.eq((s2, ), prov) # Validate that the iden calc itself is correct rawprov = ({}, [('storm', (('q', '[ test:str=foo ]'), ('user', rootiden)))]) hash = hashlib.md5(s_msgpack.en(rawprov)).hexdigest() self.eq(hash, idens[3]) # The trigger splices prov = provs[5][1] s3 = ('trig', {'cond': 'node:add', 'form': 'test:str', 'tag': None, 'prop': None}) s4 = ('storm', {'q': '[ test:int=1 ]', 'user': rootiden}) self.eq((s2, s3, s4), prov) # prop:del/node:del prov = provs[7][1] ds2 = ('storm', {'q': 'test:int | delnode', 'user': rootiden}) ds3 = ('stormcmd', {'name': 'delnode', 'argv': ()}) self.eq((ds2, ds3), prov) # Test the streaming API provstacks = await alist(core.provStacks(0, 1000)) correct = [(idens[0], provs[0]), (idens[5], provs[5]), (idens[3], provs[3]), (idens[7], provs[7])] self.eq(provstacks, correct) # Force recursion exception to be thrown q = '.created ' + '| uniq' * 257 with self.raises(s_exc.RecursionLimitHit) as cm: _ = await real.nodes(q) self.eq(cm.exception.get('type'), 'stormcmd') self.eq(cm.exception.get('info'), {'name': 'uniq', 'argv': ()}) baseframe = cm.exception.get('baseframe') name, args = baseframe self.eq(name, 'storm') self.eq(args[0], ('q', q)) recent_frames = cm.exception.get('recent_frames') self.len(6, recent_frames) for frame in recent_frames: self.eq(frame, ('stormcmd', (('argv', ()), ('name', 'uniq')))) # Run a feed function and validate the user is recorded. await core.addFeedData('syn.nodes', [(('test:int', 1138), {})]) # We have to brute force the last prov stack to get the data # Since we don't have splices to track stacks = await alist(core.provStacks(0, 1000)) feed_stack = stacks[-1] frame = feed_stack[1][1][0] self.eq(frame[0], 'feed:data') self.eq(frame[1].get('name'), 'syn.nodes') self.isin('user', frame[1])
async def test_cron(self): MONO_DELT = 1543827303.0 unixtime = datetime.datetime(year=2018, month=12, day=5, hour=7, minute=0, tzinfo=tz.utc).timestamp() s_provenance.reset() def timetime(): return unixtime def looptime(): return unixtime - MONO_DELT loop = asyncio.get_running_loop() with mock.patch.object(loop, 'time', looptime), mock.patch('time.time', timetime): async with self.getTestCoreAndProxy() as (realcore, core): outp = self.getTestOutp() async with await s_cmdr.getItemCmdr(core, outp=outp) as cmdr: # Various silliness await cmdr.runCmdLine('cron') self.true(outp.expect('Manages cron jobs in a cortex')) await cmdr.runCmdLine('cron timemachine') self.true(outp.expect('invalid choice')) await cmdr.runCmdLine('cron list') self.true(outp.expect('No cron jobs found')) await cmdr.runCmdLine('cron ls') self.true(outp.expect('No cron jobs found')) outp.clear() await cmdr.runCmdLine("cron add -M+1,beeroclock {[graph:node='*' :type=m1]}") self.true(outp.expect('failed to parse parameter')) await cmdr.runCmdLine("cron add -m nosuchmonth -d=-2 {#foo}") self.true(outp.expect('failed to parse fixed parameter')) outp.clear() await cmdr.runCmdLine("cron add -m 8nosuchmonth -d=-2 {#foo}") self.true(outp.expect('failed to parse fixed parameter')) await cmdr.runCmdLine("cron add -d Mon -m +3 {#foo}") self.true(outp.expect('provide a recurrence value with day of week')) await cmdr.runCmdLine("cron add -dMon -m June {#foo}") self.true(outp.expect('fix month or year with day of week')) await cmdr.runCmdLine("cron add -dMon -m +3 -y +2 {#foo}") self.true(outp.expect('more than 1 recurrence')) await cmdr.runCmdLine("cron add --year=2019 {#foo}") self.true(outp.expect('year may not be a fixed value')) await cmdr.runCmdLine("cron add {#foo}") self.true(outp.expect('must provide at least one optional')) await cmdr.runCmdLine("cron add -H3 -M +4 {#foo}") self.true(outp.expect('fixed unit may not be larger')) outp.clear() await cmdr.runCmdLine('cron add -d Tuesday,1 {#foo}') self.true(outp.expect('failed to parse day value')) outp.clear() await cmdr.runCmdLine('cron add -d Fri,3 {#foo}') self.true(outp.expect('failed to parse day value')) outp.clear() await cmdr.runCmdLine('cron add }') self.true(outp.expect('BadSyntax')) # add a mechanism on which we can wait... await realcore.nodes('$lib.queue.add(foo)') async def getNextFoo(): return await asyncio.wait_for(realcore.callStorm(''' $foo = $lib.queue.get(foo) ($offs, $retn) = $foo.get() $foo.cull($offs) return($retn) '''), timeout=5) ################## # Start simple: add a cron job that creates a node every minute outp.clear() await cmdr.runCmdLine("cron add -M +1 {$lib.queue.get(foo).put(bar)}") self.true(outp.expect('Created cron job')) guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime += 60 self.eq('bar', await getNextFoo()) await cmdr.runCmdLine('cron list') self.true(outp.expect('(bar)')) # Make sure it ran await cmdr.runCmdLine(f"cron mod {guid[:6]} {{$lib.queue.get(foo).put(baz)}}") self.true(outp.expect('Modified cron job')) await cmdr.runCmdLine(f"cron edit xxx {{[graph:node='*' :type=m2]}}") self.true(outp.expect('does not match')) await cmdr.runCmdLine(f"cron mod xxx yyy") self.true(outp.expect('expected second argument to start with {')) # Make sure the old one didn't run and the new query ran unixtime += 60 self.eq('baz', await getNextFoo()) outp.clear() # Delete the job await cmdr.runCmdLine(f"cron del {guid}") self.true(outp.expect('Deleted cron job')) await cmdr.runCmdLine(f"cron del xxx") self.true(outp.expect('does not match')) await cmdr.runCmdLine(f"cron rm xxx") self.true(outp.expect('does not match')) # Make sure deleted job didn't run unixtime += 60 await asyncio.sleep(0) self.eq(0, await realcore.callStorm('return($lib.queue.get(foo).size())')) # Test fixed minute, i.e. every hour at 17 past unixtime = datetime.datetime(year=2018, month=12, day=5, hour=7, minute=10, tzinfo=tz.utc).timestamp() await cmdr.runCmdLine("cron add -M 17 {$lib.queue.get(foo).put(faz)}") guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime += 7 * MINSECS self.eq('faz', await getNextFoo()) await cmdr.runCmdLine(f"cron del {guid}") ################## # Test day increment await cmdr.runCmdLine("cron add -d +2 {$lib.queue.get(foo).put(d1)}") self.true(outp.expect('Created cron job')) guid1 = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime += DAYSECS # Make sure it *didn't* run await asyncio.sleep(0) self.eq(0, await realcore.callStorm('return($lib.queue.get(foo).size())')) unixtime += DAYSECS self.eq('d1', await getNextFoo()) unixtime += DAYSECS * 2 outp.clear() self.eq('d1', await getNextFoo()) await cmdr.runCmdLine(f"cron del {guid1}") outp.expect('Deleted cron job') ################## # Test fixed day of week: every Monday and Thursday at 3am unixtime = datetime.datetime(year=2018, month=12, day=11, hour=7, minute=10, tzinfo=tz.utc).timestamp() # A Tuesday outp.clear() await cmdr.runCmdLine("cron add -H 3 -d Mon,Thursday {$lib.queue.get(foo).put(d2)}") self.true(outp.expect('Created cron job')) guid2 = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime = datetime.datetime(year=2018, month=12, day=12, hour=3, minute=10, tzinfo=tz.utc).timestamp() # Now Wednesday outp.clear() await cmdr.runCmdLine(f'cron stat {guid2}') self.true(outp.expect('last start time: Never')) unixtime = datetime.datetime(year=2018, month=12, day=13, hour=3, minute=10, tzinfo=tz.utc).timestamp() # Now Thursday self.eq('d2', await getNextFoo()) outp.clear() await cmdr.runCmdLine(f'cron stat {guid2}') self.true(outp.expect('last start time: 2018')) self.true(outp.expect('dayofweek 0')) outp.clear() await cmdr.runCmdLine(f"cron del {guid2}") outp.expect('Deleted cron job') await cmdr.runCmdLine("cron add -H 3 -d Noday {[graph:node='*' :type=d2]}") self.true(outp.expect('failed to parse day value "Noday"')) ################## # Test fixed day of month: second-to-last day of month await cmdr.runCmdLine("cron add -d-2 -mDec {$lib.queue.get(foo).put(d3)}") guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime = datetime.datetime(year=2018, month=12, day=29, hour=0, minute=0, tzinfo=tz.utc).timestamp() # Now Thursday unixtime += DAYSECS self.eq('d3', await getNextFoo()) outp.clear() await cmdr.runCmdLine(f"cron del {guid}") outp.expect('Deleted cron job') ################## # Test month increment outp.clear() await cmdr.runCmdLine("cron add -m +2 -d=4 {$lib.queue.get(foo).put(month1)}") guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] self.true(outp.expect('Created cron job')) unixtime = datetime.datetime(year=2019, month=2, day=4, hour=0, minute=0, tzinfo=tz.utc).timestamp() # Now Thursday self.eq('month1', await getNextFoo()) outp.clear() await cmdr.runCmdLine(f"cron del {guid}") outp.expect('Deleted cron job') ################## # Test year increment outp.clear() await cmdr.runCmdLine("cron add -y +2 {$lib.queue.get(foo).put(year1)}") guid2 = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] self.true(outp.expect('Created cron job')) unixtime = datetime.datetime(year=2021, month=1, day=1, hour=0, minute=0, tzinfo=tz.utc).timestamp() # Now Thursday self.eq('year1', await getNextFoo()) outp.clear() await cmdr.runCmdLine(f'cron stat {guid2[:6]}') self.true(outp.expect("{'month': 1, 'hour': 0, 'minute': 0, 'dayofmonth': 1}")) outp.clear() await cmdr.runCmdLine(f"cron del {guid2}") outp.expect('Deleted cron job') # Make sure second-to-last day works for February outp.clear() await cmdr.runCmdLine("cron add -m February -d=-2 {$lib.queue.get(foo).put(year2)}") self.true(outp.expect('Created cron job')) unixtime = datetime.datetime(year=2021, month=2, day=27, hour=0, minute=0, tzinfo=tz.utc).timestamp() # Now Thursday self.eq('year2', await getNextFoo()) ################## # Test 'at' command outp.clear() await cmdr.runCmdLine('at') self.true(outp.expect('Adds a non-recurring')) await cmdr.runCmdLine('at --not-a-real-flag') self.true(outp.expect('the following arguments')) await cmdr.runCmdLine('at {#foo} {#bar}') self.true(outp.expect('only a single query')) await cmdr.runCmdLine('at {#foo}') self.true(outp.expect('at least')) await cmdr.runCmdLine('at +1') self.true(outp.expect('missing unit')) await cmdr.runCmdLine('at +1parsec') self.true(outp.expect('Trouble parsing')) await cmdr.runCmdLine('at +1day') self.true(outp.expect('Missing query')) await cmdr.runCmdLine("at +5 minutes {$lib.queue.get(foo).put(at1)}") unixtime += 5 * MINSECS self.eq('at1', await getNextFoo()) await cmdr.runCmdLine("at +1 day +7 days {$lib.queue.get(foo).put(at2)}") guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime += DAYSECS self.eq('at2', await getNextFoo()) unixtime += 6 * DAYSECS + 1 self.eq('at2', await getNextFoo()) await cmdr.runCmdLine("at 202104170415 {$lib.queue.get(foo).put(at3)}") unixtime = datetime.datetime(year=2021, month=4, day=17, hour=4, minute=15, tzinfo=tz.utc).timestamp() # Now Thursday self.eq('at3', await getNextFoo()) outp.clear() ################## # Test 'stat' command await cmdr.runCmdLine(f'cron stat xxx') self.true(outp.expect('provided iden does not match any')) await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect('last result: finished successfully with 0 nodes')) self.true(outp.expect('entries: <None>')) ################## # Test 'enable' 'disable' commands await cmdr.runCmdLine(f'cron enable xxx') self.true(outp.expect('provided iden does not match any')) outp.clear() await cmdr.runCmdLine(f'cron disable xxx') self.true(outp.expect('provided iden does not match any')) outp.clear() await cmdr.runCmdLine(f'cron disable {guid[:6]}') await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect(f'enabled: N')) outp.clear() await cmdr.runCmdLine(f'cron enable {guid[:6]}') await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect(f'enabled: Y')) outp.clear() ################### # Delete an expired at job outp.clear() await cmdr.runCmdLine(f"cron del {guid}") self.true(outp.expect('Deleted cron job')) ################## # Test the aliases outp.clear() await cmdr.runCmdLine('cron add --hourly 15 {#bar}') guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect("{'minute': 15}")) outp.clear() await cmdr.runCmdLine('cron add --daily 05:47 {#bar}') guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect("{'hour': 5, 'minute': 47")) outp.clear() await cmdr.runCmdLine('cron add --monthly=-1:12:30 {#bar}') guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect("{'hour': 12, 'minute': 30, 'dayofmonth': -1}")) outp.clear() await cmdr.runCmdLine('cron add --yearly 04:17:12:30 {#bar}') guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect("{'month': 4, 'hour': 12, 'minute': 30, 'dayofmonth': 17}")) outp.clear() await cmdr.runCmdLine('cron add --yearly 04:17:12 {#bar}') self.true(outp.expect('Failed to parse parameter')) outp.clear() await cmdr.runCmdLine('cron add --daily xx:xx {#bar}') self.true(outp.expect('Failed to parse ..ly parameter')) outp.clear() await cmdr.runCmdLine('cron add --hourly 1 -M 17 {#bar}') self.true(outp.expect('may not use both')) # Test manipulating cron jobs as another user bond = await realcore.auth.addUser('bond') async with realcore.getLocalProxy(user='******') as tcore: toutp = self.getTestOutp() tcmdr = await s_cmdr.getItemCmdr(tcore, outp=toutp) await tcmdr.runCmdLine('cron list') self.true(toutp.expect('No cron jobs found')) toutp.clear() await tcmdr.runCmdLine(f'cron disable {guid[:6]}') self.true(toutp.expect('provided iden does not match')) toutp.clear() await tcmdr.runCmdLine(f'cron enable {guid[:6]}') self.true(toutp.expect('provided iden does not match')) toutp.clear() await tcmdr.runCmdLine(f'cron edit {guid[:6]} {{#foo}}') self.true(toutp.expect('provided iden does not match')) toutp.clear() await tcmdr.runCmdLine(f'cron del {guid[:6]}') self.true(toutp.expect('provided iden does not match')) # Give explicit perm await core.addUserRule(bond.iden, (True, ('cron', 'get'))) toutp.clear() await tcmdr.runCmdLine('cron list') self.true(toutp.expect('root')) await core.addUserRule(bond.iden, (True, ('cron', 'set'))) toutp.clear() await tcmdr.runCmdLine(f'cron disable {guid[:6]}') self.true(toutp.expect('Disabled cron job')) toutp.clear() await tcmdr.runCmdLine(f'cron enable {guid[:6]}') self.true(toutp.expect('Enabled cron job')) toutp.clear() await tcmdr.runCmdLine(f'cron edit {guid[:6]} {{#foo}}') self.true(toutp.expect('Modified cron job')) await core.addUserRule(bond.iden, (True, ('cron', 'del'))) toutp.clear() await tcmdr.runCmdLine(f'cron del {guid[:6]}') self.true(toutp.expect('Deleted cron job'))
async def test_cron(self): MONO_DELT = 1543827303.0 unixtime = datetime.datetime(year=2018, month=12, day=5, hour=7, minute=0, tzinfo=tz.utc).timestamp() sync = asyncio.Event() lastquery = None s_provenance.reset() def timetime(): return unixtime def looptime(): return unixtime - MONO_DELT async def myeval(query, user=None): nonlocal lastquery lastquery = query sync.set() return yield None loop = asyncio.get_running_loop() with mock.patch.object(loop, 'time', looptime), mock.patch('time.time', timetime): async with self.getTestCoreAndProxy() as (realcore, core): outp = self.getTestOutp() async with await s_cmdr.getItemCmdr(core, outp=outp) as cmdr: async def waitForCron(guid): ''' Because the wall clock is "frozen" for this test unless we manually advance it, we can't sleep non-zero amounts. However, we are running in the same asyncio loop as the agenda. Just sleep(0) in a loop until the cron job is not running anymore ''' for _ in range(30): await asyncio.sleep(0) crons = await core.listCronJobs() cron = [c for c in crons if c.get('iden') == guid][0] if not cron['isrunning']: break else: # the cron job didn't finish after ten sleeps?! self.true(0) # Various silliness await cmdr.runCmdLine('cron') self.true(outp.expect('Manages cron jobs in a cortex')) await cmdr.runCmdLine('cron timemachine') self.true(outp.expect('invalid choice')) await cmdr.runCmdLine('cron list') self.true(outp.expect('No cron jobs found')) await cmdr.runCmdLine('cron ls') self.true(outp.expect('No cron jobs found')) outp.clear() await cmdr.runCmdLine( "cron add -M+1,beeroclock {[graph:node='*' :type=m1]}") self.true(outp.expect('failed to parse parameter')) await cmdr.runCmdLine( "cron add -m nosuchmonth -d=-2 {#foo}") self.true(outp.expect('failed to parse fixed parameter')) outp.clear() await cmdr.runCmdLine( "cron add -m 8nosuchmonth -d=-2 {#foo}") self.true(outp.expect('failed to parse fixed parameter')) await cmdr.runCmdLine("cron add -d=, {#foo}") self.true(outp.expect('failed to parse day value')) await cmdr.runCmdLine("cron add -dMon -m +3 {#foo}") self.true( outp.expect( 'provide a recurrence value with day of week')) await cmdr.runCmdLine("cron add -dMon -m June {#foo}") self.true( outp.expect('fix month or year with day of week')) await cmdr.runCmdLine("cron add -dMon -m +3 -y +2 {#foo}") self.true(outp.expect('more than 1 recurrence')) await cmdr.runCmdLine("cron add --year=2019 {#foo}") self.true(outp.expect('year may not be a fixed value')) await cmdr.runCmdLine("cron add {#foo}") self.true( outp.expect('must provide at least one optional')) await cmdr.runCmdLine("cron add -H3 -M +4 {#foo}") self.true(outp.expect('fixed unit may not be larger')) outp.clear() await cmdr.runCmdLine('cron add -d Tuesday,1 {#foo}') self.true(outp.expect('failed to parse day value')) outp.clear() await cmdr.runCmdLine('cron add -d Fri,3 {#foo}') self.true(outp.expect('failed to parse day value')) outp.clear() await cmdr.runCmdLine('cron add }') self.true(outp.expect('BadSyntax')) ################## # Start simple: add a cron job that creates a node every minute outp.clear() await cmdr.runCmdLine( "cron add -M +1 {[graph:node='*' :type=m1]}") self.true(outp.expect('Created cron job')) guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime += 60 await asyncio.sleep(0) await cmdr.runCmdLine('cron list') self.true(outp.expect(':type=m1')) # Make sure it ran await self.agenlen(1, core.eval('graph:node:type=m1')) await cmdr.runCmdLine( f"cron mod {guid[:6]} {{[graph:node='*' :type=m2]}}") self.true(outp.expect('Modified cron job')) await cmdr.runCmdLine( f"cron edit xxx {{[graph:node='*' :type=m2]}}") self.true(outp.expect('does not match')) await cmdr.runCmdLine(f"cron mod xxx yyy") self.true( outp.expect( 'expected second argument to start with {')) # Make sure the old one didn't run and the new query ran unixtime += 60 await asyncio.sleep(0) outp.clear() await self.agenlen(1, core.eval('graph:node:type=m1')) await self.agenlen(1, core.eval('graph:node:type=m2')) outp.clear() # Delete the job await cmdr.runCmdLine(f"cron del {guid}") self.true(outp.expect('Deleted cron job')) await cmdr.runCmdLine(f"cron del xxx") self.true(outp.expect('does not match')) await cmdr.runCmdLine(f"cron rm xxx") self.true(outp.expect('does not match')) # Make sure deleted job didn't run unixtime += 60 await asyncio.sleep(0) await self.agenlen(1, core.eval('graph:node:type=m1')) await self.agenlen(1, core.eval('graph:node:type=m2')) # Test fixed minute, i.e. every hour at 17 past unixtime = datetime.datetime(year=2018, month=12, day=5, hour=7, minute=10, tzinfo=tz.utc).timestamp() await cmdr.runCmdLine( "cron add -M 17 {[graph:node='*' :type=m3]}") guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime += 7 * MINSECS await asyncio.sleep(0) # Make sure it runs. We add the cron list to give the cron scheduler a chance to run await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=m3')) await cmdr.runCmdLine(f"cron del {guid}") ################## # Test day increment await cmdr.runCmdLine( "cron add -d +2 {[graph:node='*' :type=d1]}") self.true(outp.expect('Created cron job')) guid1 = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime += DAYSECS await asyncio.sleep(0) # Make sure it *didn't* run await self.agenlen(0, core.eval('graph:node:type=d1')) unixtime += DAYSECS await asyncio.sleep(0) # Make sure it runs. We add the cron list to give the cron scheduler a chance to run await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=d1')) unixtime += DAYSECS * 2 await asyncio.sleep(0) await cmdr.runCmdLine('cron list') await self.agenlen(2, core.eval('graph:node:type=d1')) ################## # Test fixed day of week: every Monday and Thursday at 3am unixtime = datetime.datetime( year=2018, month=12, day=11, hour=7, minute=10, tzinfo=tz.utc).timestamp() # A Tuesday await asyncio.sleep(0) await cmdr.runCmdLine( "cron add -H 3 -d Mon,Thursday {[graph:node='*' :type=d2]}" ) guid2 = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime = datetime.datetime( year=2018, month=12, day=13, hour=3, minute=10, tzinfo=tz.utc).timestamp() # Now Thursday await asyncio.sleep(0) await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=d2')) await cmdr.runCmdLine(f"cron del {guid1}") await cmdr.runCmdLine(f"cron del {guid2}") await cmdr.runCmdLine( "cron add -H 3 -d Noday {[graph:node='*' :type=d2]}") self.true(outp.expect('failed to parse day value "Noday"')) ################## # Test fixed day of month: second-to-last day of month await cmdr.runCmdLine( "cron add -d-2 -mDec {[graph:node='*' :type=d3]}") guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime = datetime.datetime( year=2018, month=12, day=29, hour=0, minute=0, tzinfo=tz.utc).timestamp() # Now Thursday await asyncio.sleep(0) await cmdr.runCmdLine('cron list') await self.agenlen(0, core.eval('graph:node:type=d3') ) # Not yet unixtime += DAYSECS await asyncio.sleep(0) await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=d3')) await cmdr.runCmdLine(f"cron del {guid}") ################## # Test month increment await cmdr.runCmdLine( "cron add -m +2 -d=4 {[graph:node='*' :type=month1]}") unixtime = datetime.datetime( year=2019, month=2, day=4, hour=0, minute=0, tzinfo=tz.utc).timestamp() # Now Thursday await asyncio.sleep(0) await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=month1')) ################## # Test year increment await cmdr.runCmdLine( "cron add -y +2 {[graph:node='*' :type=year1]}") guid2 = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime = datetime.datetime( year=2021, month=1, day=1, hour=0, minute=0, tzinfo=tz.utc).timestamp() # Now Thursday await asyncio.sleep(0) await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=year1')) # Make sure second-to-last day works for February await cmdr.runCmdLine( "cron add -m February -d=-2 {[graph:node='*' :type=year2]}" ) unixtime = datetime.datetime( year=2021, month=2, day=27, hour=0, minute=0, tzinfo=tz.utc).timestamp() # Now Thursday await asyncio.sleep(0) await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=year2')) ################## # Test 'at' command outp.clear() await cmdr.runCmdLine('at') self.true(outp.expect('Adds a non-recurring')) await cmdr.runCmdLine('at --not-a-real-flag') self.true(outp.expect('the following arguments')) await cmdr.runCmdLine('at {#foo} {#bar}') self.true(outp.expect('only a single query')) await cmdr.runCmdLine('at {#foo}') self.true(outp.expect('at least')) await cmdr.runCmdLine('at +1') self.true(outp.expect('missing unit')) await cmdr.runCmdLine('at +1parsec') self.true(outp.expect('Trouble parsing')) await cmdr.runCmdLine('at +1day') self.true(outp.expect('Missing query')) await cmdr.runCmdLine( "at +5 minutes {[graph:node='*' :type=at1]}") unixtime += 5 * MINSECS await asyncio.sleep(0) await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=at1')) await cmdr.runCmdLine( "at +1 day +7 days {[graph:node='*' :type=at2]}") guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime += DAYSECS await waitForCron(guid) await self.agenlen(1, core.eval('graph:node:type=at2')) unixtime += 6 * DAYSECS + 1 await asyncio.sleep(0) await cmdr.runCmdLine('cron list') await self.agenlen(2, core.eval('graph:node:type=at2')) await cmdr.runCmdLine( "at 202104170415 {[graph:node='*' :type=at3]}") unixtime = datetime.datetime( year=2021, month=4, day=17, hour=4, minute=15, tzinfo=tz.utc).timestamp() # Now Thursday await asyncio.sleep(0) await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=at3')) outp.clear() ################## # Test 'stat' command await cmdr.runCmdLine(f'cron stat xxx') self.true(outp.expect('provided iden does not match any')) await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true( outp.expect( 'last result: finished successfully with 1 nodes' )) self.true(outp.expect('entries: <None>')) await cmdr.runCmdLine(f'cron stat {guid2[:6]}') self.true( outp.expect( "{'month': 1, 'hour': 0, 'minute': 0, 'dayofmonth': 1}" )) outp.clear() ################## # Test 'enable' 'disable' commands await cmdr.runCmdLine(f'cron enable xxx') self.true(outp.expect('provided iden does not match any')) outp.clear() await cmdr.runCmdLine(f'cron disable xxx') self.true(outp.expect('provided iden does not match any')) outp.clear() await cmdr.runCmdLine(f'cron disable {guid[:6]}') await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect(f'enabled: N')) outp.clear() await cmdr.runCmdLine(f'cron enable {guid[:6]}') await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect(f'enabled: Y')) outp.clear() ################### # Delete an expired at job outp.clear() await cmdr.runCmdLine(f"cron del {guid}") self.true(outp.expect('Deleted cron job')) ################## # Test the aliases outp.clear() await cmdr.runCmdLine('cron add --hourly 15 {#bar}') guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect("{'minute': 15}")) outp.clear() await cmdr.runCmdLine('cron add --daily 05:47 {#bar}') guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect("{'hour': 5, 'minute': 47")) outp.clear() await cmdr.runCmdLine('cron add --monthly=-1:12:30 {#bar}') guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true( outp.expect( "{'hour': 12, 'minute': 30, 'dayofmonth': -1}")) outp.clear() await cmdr.runCmdLine( 'cron add --yearly 04:17:12:30 {#bar}') guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true( outp.expect( "{'month': 4, 'hour': 12, 'minute': 30, 'dayofmonth': 17}" )) outp.clear() await cmdr.runCmdLine('cron add --yearly 04:17:12 {#bar}') self.true(outp.expect('Failed to parse parameter')) outp.clear() await cmdr.runCmdLine('cron add --daily xx:xx {#bar}') self.true(outp.expect('Failed to parse ..ly parameter')) outp.clear() await cmdr.runCmdLine('cron add --hourly 1 -M 17 {#bar}') self.true(outp.expect('may not use both')) # Test manipulating cron jobs as another user await realcore.auth.addUser('bond') async with realcore.getLocalProxy(user='******') as tcore: toutp = self.getTestOutp() tcmdr = await s_cmdr.getItemCmdr(tcore, outp=toutp) await tcmdr.runCmdLine('cron list') self.true(toutp.expect('No cron jobs found')) toutp.clear() await tcmdr.runCmdLine(f'cron disable {guid[:6]}') self.true(toutp.expect('provided iden does not match')) toutp.clear() await tcmdr.runCmdLine(f'cron enable {guid[:6]}') self.true(toutp.expect('provided iden does not match')) toutp.clear() await tcmdr.runCmdLine(f'cron edit {guid[:6]} {{#foo}}') self.true(toutp.expect('provided iden does not match')) toutp.clear() await tcmdr.runCmdLine(f'cron del {guid[:6]}') self.true(toutp.expect('provided iden does not match')) # Give explicit perm await core.addUserRule('bond', (True, ('cron', 'get'))) toutp.clear() await tcmdr.runCmdLine('cron list') self.true(toutp.expect('root')) await core.addUserRule('bond', (True, ('cron', 'set'))) toutp.clear() await tcmdr.runCmdLine(f'cron disable {guid[:6]}') self.true(toutp.expect('Disabled cron job')) toutp.clear() await tcmdr.runCmdLine(f'cron enable {guid[:6]}') self.true(toutp.expect('Enabled cron job')) toutp.clear() await tcmdr.runCmdLine(f'cron edit {guid[:6]} {{#foo}}') self.true(toutp.expect('Modified cron job')) await core.addUserRule('bond', (True, ('cron', 'del'))) toutp.clear() await tcmdr.runCmdLine(f'cron del {guid[:6]}') self.true(toutp.expect('Deleted cron job'))
async def test_cron(self): MONO_DELT = 1543827303.0 unixtime = datetime.datetime(year=2018, month=12, day=5, hour=7, minute=0, tzinfo=tz.utc).timestamp() sync = asyncio.Event() lastquery = None s_provenance.reset() def timetime(): return unixtime def looptime(): return unixtime - MONO_DELT async def myeval(query, user=None): nonlocal lastquery lastquery = query sync.set() return yield None loop = asyncio.get_running_loop() with mock.patch.object(loop, 'time', looptime), mock.patch('time.time', timetime): async with self.getTestCoreAndProxy() as (realcore, core): outp = self.getTestOutp() async with await s_cmdr.getItemCmdr(core, outp=outp) as cmdr: # Various silliness await cmdr.runCmdLine('cron') self.true(outp.expect('Manages cron jobs in a cortex')) await cmdr.runCmdLine('cron timemachine') self.true(outp.expect('invalid choice')) await cmdr.runCmdLine('cron list') self.true(outp.expect('No cron jobs found')) outp.clear() await cmdr.runCmdLine("cron add -M+1,beeroclock {[graph:node='*' :type=m1]}") self.true(outp.expect('failed to parse parameter')) await cmdr.runCmdLine("cron add -m nosuchmonth -d=-2 {#foo}") self.true(outp.expect('failed to parse fixed parameter')) outp.clear() await cmdr.runCmdLine("cron add -m 8nosuchmonth -d=-2 {#foo}") self.true(outp.expect('failed to parse fixed parameter')) await cmdr.runCmdLine("cron add -d=, {#foo}") self.true(outp.expect('failed to parse day value')) await cmdr.runCmdLine("cron add -dMon -m +3 {#foo}") self.true(outp.expect('provide a recurrence value with day of week')) await cmdr.runCmdLine("cron add -dMon -m June {#foo}") self.true(outp.expect('fix month or year with day of week')) await cmdr.runCmdLine("cron add -dMon -m +3 -y +2 {#foo}") self.true(outp.expect('more than 1 recurrence')) await cmdr.runCmdLine("cron add --year=2019 {#foo}") self.true(outp.expect('year may not be a fixed value')) await cmdr.runCmdLine("cron add {#foo}") self.true(outp.expect('must provide at least one optional')) await cmdr.runCmdLine("cron add -H3 -M +4 {#foo}") self.true(outp.expect('fixed unit may not be larger')) outp.clear() await cmdr.runCmdLine('cron add -d Tuesday,1 {#foo}') self.true(outp.expect('failed to parse day value')) outp.clear() await cmdr.runCmdLine('cron add -d Fri,3 {#foo}') self.true(outp.expect('failed to parse day value')) outp.clear() await cmdr.runCmdLine('cron add }') self.true(outp.expect('query parameter must start with {')) ################## oldsplices = len(await alist(core.splices(0, 1000))) # Start simple: add a cron job that creates a node every minute outp.clear() await cmdr.runCmdLine("cron add -M +1 {[graph:node='*' :type=m1]}") self.true(outp.expect('Created cron job')) guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime += 60 await cmdr.runCmdLine('cron list') self.true(outp.expect(':type=m1')) # Make sure it ran await self.agenlen(1, core.eval('graph:node:type=m1')) # Make sure the provenance of the new splices looks right splices = await alist(core.splices(oldsplices, 1000)) self.gt(len(splices), 1) aliases = [splice[1]['prov'] for splice in splices] self.true(all(a == aliases[0] for a in aliases)) prov = await core.getProvStack(aliases[0]) rootiden = prov[1][1][1]['user'] correct = ({}, ( ('cron', {'iden': guid}), ('storm', {'q': "[graph:node='*' :type=m1]", 'user': rootiden}))) self.eq(prov, correct) await cmdr.runCmdLine(f"cron mod {guid[:6]} {{[graph:node='*' :type=m2]}}") self.true(outp.expect('Modified cron job')) await cmdr.runCmdLine(f"cron mod xxx {{[graph:node='*' :type=m2]}}") self.true(outp.expect('does not match')) await cmdr.runCmdLine(f"cron mod xxx yyy") self.true(outp.expect('expected second argument to start with {')) # Make sure the old one didn't run and the new query ran unixtime += 60 await self.agenlen(1, core.eval('graph:node:type=m1')) await self.agenlen(1, core.eval('graph:node:type=m2')) outp.clear() # Delete the job await cmdr.runCmdLine(f"cron del {guid}") self.true(outp.expect('Deleted cron job')) await cmdr.runCmdLine(f"cron del xxx") self.true(outp.expect('does not match')) # Make sure deleted job didn't run unixtime += 60 await self.agenlen(1, core.eval('graph:node:type=m1')) await self.agenlen(1, core.eval('graph:node:type=m2')) # Test fixed minute, i.e. every hour at 17 past unixtime = datetime.datetime(year=2018, month=12, day=5, hour=7, minute=10, tzinfo=tz.utc).timestamp() await cmdr.runCmdLine("cron add -M 17 {[graph:node='*' :type=m3]}") guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime += 7 * MINSECS # Make sure it runs. We add the cron list to give the cron scheduler a chance to run await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=m3')) await cmdr.runCmdLine(f"cron del {guid}") ################## # Test day increment await cmdr.runCmdLine("cron add -d +2 {[graph:node='*' :type=d1]}") self.true(outp.expect('Created cron job')) guid1 = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime += DAYSECS # Make sure it *didn't* run await self.agenlen(0, core.eval('graph:node:type=d1')) unixtime += DAYSECS # Make sure it runs. We add the cron list to give the cron scheduler a chance to run await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=d1')) unixtime += DAYSECS * 2 await cmdr.runCmdLine('cron list') await self.agenlen(2, core.eval('graph:node:type=d1')) ################## # Test fixed day of week: every Monday and Thursday at 3am unixtime = datetime.datetime(year=2018, month=12, day=11, hour=7, minute=10, tzinfo=tz.utc).timestamp() # A Tuesday await cmdr.runCmdLine("cron add -H 3 -d Mon,Thursday {[graph:node='*' :type=d2]}") guid2 = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime = datetime.datetime(year=2018, month=12, day=13, hour=3, minute=10, tzinfo=tz.utc).timestamp() # Now Thursday await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=d2')) await cmdr.runCmdLine(f"cron del {guid1}") await cmdr.runCmdLine(f"cron del {guid2}") await cmdr.runCmdLine("cron add -H 3 -d Noday {[graph:node='*' :type=d2]}") self.true(outp.expect('failed to parse day value "Noday"')) ################## # Test fixed day of month: second-to-last day of month await cmdr.runCmdLine("cron add -d-2 -mDec {[graph:node='*' :type=d3]}") guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime = datetime.datetime(year=2018, month=12, day=29, hour=0, minute=0, tzinfo=tz.utc).timestamp() # Now Thursday await cmdr.runCmdLine('cron list') await self.agenlen(0, core.eval('graph:node:type=d3')) # Not yet unixtime += DAYSECS await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=d3')) await cmdr.runCmdLine(f"cron del {guid}") ################## # Test month increment await cmdr.runCmdLine("cron add -m +2 -d=4 {[graph:node='*' :type=month1]}") unixtime = datetime.datetime(year=2019, month=2, day=4, hour=0, minute=0, tzinfo=tz.utc).timestamp() # Now Thursday await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=month1')) ################## # Test year increment await cmdr.runCmdLine("cron add -y +2 {[graph:node='*' :type=year1]}") guid2 = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime = datetime.datetime(year=2021, month=1, day=1, hour=0, minute=0, tzinfo=tz.utc).timestamp() # Now Thursday await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=year1')) # Make sure second-to-last day works for February await cmdr.runCmdLine("cron add -m February -d=-2 {[graph:node='*' :type=year2]}") unixtime = datetime.datetime(year=2021, month=2, day=27, hour=0, minute=0, tzinfo=tz.utc).timestamp() # Now Thursday await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=year2')) ################## # Test 'at' command outp.clear() await cmdr.runCmdLine('at') self.true(outp.expect('Adds a non-recurring')) await cmdr.runCmdLine('at --not-a-real-flag') self.true(outp.expect('the following arguments')) await cmdr.runCmdLine('at {#foo} {#bar}') self.true(outp.expect('only a single query')) await cmdr.runCmdLine('at {#foo}') self.true(outp.expect('at least')) await cmdr.runCmdLine('at +1') self.true(outp.expect('missing unit')) await cmdr.runCmdLine('at +1parsec') self.true(outp.expect('Trouble parsing')) await cmdr.runCmdLine('at +1day') self.true(outp.expect('Missing query')) await cmdr.runCmdLine("at +5 minutes {[graph:node='*' :type=at1]}") unixtime += 5 * MINSECS await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=at1')) await cmdr.runCmdLine("at +1 day +7 days {[graph:node='*' :type=at2]}") guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] unixtime += DAYSECS await self.agenlen(1, core.eval('graph:node:type=at2')) unixtime += 6 * DAYSECS + 1 await cmdr.runCmdLine('cron list') await self.agenlen(2, core.eval('graph:node:type=at2')) await cmdr.runCmdLine("at 202104170415 {[graph:node='*' :type=at3]}") unixtime = datetime.datetime(year=2021, month=4, day=17, hour=4, minute=15, tzinfo=tz.utc).timestamp() # Now Thursday await cmdr.runCmdLine('cron list') await self.agenlen(1, core.eval('graph:node:type=at3')) ################## # Test 'stat' command await cmdr.runCmdLine(f'cron stat xxx') self.true(outp.expect('provided iden does not match any')) await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect('last result: finished successfully with 1 nodes')) self.true(outp.expect('entries: <None>')) await cmdr.runCmdLine(f'cron stat {guid2[:6]}') self.true(outp.expect("{'month': 1, 'hour': 0, 'minute': 0, 'dayofmonth': 1}")) ################## # Delete an expired at job outp.clear() await cmdr.runCmdLine(f"cron del {guid}") self.true(outp.expect('Deleted cron job')) ################## # Test the aliases outp.clear() await cmdr.runCmdLine('cron add --hourly 15 {#bar}') guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect("{'minute': 15}")) outp.clear() await cmdr.runCmdLine('cron add --daily 05:47 {#bar}') guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect("{'hour': 5, 'minute': 47")) outp.clear() await cmdr.runCmdLine('cron add --monthly=-1:12:30 {#bar}') guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect("{'hour': 12, 'minute': 30, 'dayofmonth': -1}")) outp.clear() await cmdr.runCmdLine('cron add --yearly 04:17:12:30 {#bar}') guid = outp.mesgs[-1].strip().rsplit(' ', 1)[-1] await cmdr.runCmdLine(f'cron stat {guid[:6]}') self.true(outp.expect("{'month': 4, 'hour': 12, 'minute': 30, 'dayofmonth': 17}")) outp.clear() await cmdr.runCmdLine('cron add --yearly 04:17:12 {#bar}') self.true(outp.expect('Failed to parse parameter')) outp.clear() await cmdr.runCmdLine('cron add --daily xx:xx {#bar}') self.true(outp.expect('Failed to parse ..ly parameter')) outp.clear() await cmdr.runCmdLine('cron add --hourly 1 -M 17 {#bar}') self.true(outp.expect('may not use both'))