def test_nested_tree_missing_child(self): """ MatchesPsTree can detect a missing child process. """ ps_tree = PsTree(PsRow(1, 0, 'root', 'tini -- app'), [ PsTree(PsRow(2, 1, 'root', 'app --arg'), [ PsTree(PsRow(3, 2, 'appuser', 'app --child1')), ]), PsTree(PsRow(5, 1, 'root', 'app2 --arg'), [ PsTree(PsRow(6, 5, 'root', 'app2 --child')), ]), ]) matcher = MatchesPsTree('root', 'tini -- app', pid=1, children=[ MatchesPsTree('root', 'app --arg', children=[ MatchesPsTree('appuser', 'app --child1'), MatchesPsTree('appuser', 'app --child2'), ]), MatchesPsTree('root', 'app2 --arg', children=[ MatchesPsTree('root', 'app2 --child'), ]), ]) mm = matcher.match(ps_tree).describe() assert "mismatches in children:" in mm assert "There was 1 matcher left over:" in mm
def test_header_alignment_long_pids(self): """ For some header alignments, the pids may extend beyond the left edge of the column headers. """ ps_rows = [ PsRow(1, 0, 'root', 'tini -- foo'), PsRow(10000, 1, 'root', 'foo'), PsRow(10001, 10000, 'root', 'bar'), ] busybox_con = FakePsContainer([ b'PID PPID RUSER COMMAND', b' 1 0 root tini -- foo', b'10000 1 root foo', b'10001 10000 root bar', b' 6 0 root ps ax -o pid,ppid,ruser,args', ]) self.assertEqual(ps_rows, list_container_processes(busybox_con)) debian_con = FakePsContainer([ b' PID PPID RUSER COMMAND', b' 1 0 root tini -- foo', b'10000 1 root foo', b'10001 10000 root bar', b' 6 0 root ps ax -o pid,ppid,ruser,args', ]) self.assertEqual(ps_rows, list_container_processes(debian_con))
def test_fields(self): """ A PsRow can be created from field values of various types. """ ps_row = PsRow('1', '0', 'root', 'tini -- true') self.assertEqual( (ps_row.pid, ps_row.ppid, ps_row.ruser, ps_row.args), (1, 0, 'root', 'tini -- true')) ps_row = PsRow(1, 0, 'root', 'tini -- true') self.assertEqual( (ps_row.pid, ps_row.ppid, ps_row.ruser, ps_row.args), (1, 0, 'root', 'tini -- true'))
def test_single_process(self): """ We can build a PsTree for a single process. """ ps_row = PsRow('1', '0', 'root', 'tini -- echo "hi"') ps_tree = build_process_tree([ps_row]) self.assertEqual(ps_tree, PsTree(ps_row, children=[]))
def test_many_processes(self): """ If there are a lot of processes running in a container, we return all of them. """ con = FakePsContainer([ b' PID PPID RUSER COMMAND', b' 1 0 root tini -- django-entrypoint.sh args', b' 6 1 django gunicorn master args', b' 17 6 root nginx master args', b' 18 6 django celery worker args', b' 19 6 django celery beat args', b' 26 17 nginx nginx worker args', b' 34 6 django gunicorn worker args', b' 48 0 root ps ax -o pid,ppid,ruser,args', ]) self.assertEqual(list_container_processes(con), [ PsRow(1, 0, 'root', 'tini -- django-entrypoint.sh args'), PsRow(6, 1, 'django', 'gunicorn master args'), PsRow(17, 6, 'root', 'nginx master args'), PsRow(18, 6, 'django', 'celery worker args'), PsRow(19, 6, 'django', 'celery beat args'), PsRow(26, 17, 'nginx', 'nginx worker args'), PsRow(34, 6, 'django', 'gunicorn worker args'), ])
def test_using_assert_that(self): """ MatchesPsTree can be used with assert_that() from testtools. """ pst = PsTree(PsRow(1, 0, 'root', 'tini -- app'), [ PsTree(PsRow(2, 1, 'root', 'app --arg'), [ PsTree(PsRow(3, 2, 'appuser', 'app --child1')), PsTree(PsRow(4, 2, 'appuser', 'app --child2')), ]), PsTree(PsRow(5, 1, 'root', 'app2 --arg'), [ PsTree(PsRow(6, 5, 'root', 'app2 --child')), ]), ]) # This passes if the MatchesPsTree matcher matches. assert_that(pst, MatchesPsTree('root', 'tini -- app', children=[ MatchesPsTree('root', 'app --arg', children=[ MatchesPsTree('appuser', 'app --child1'), MatchesPsTree('appuser', 'app --child2'), ]), MatchesPsTree('root', 'app2 --arg', children=[ MatchesPsTree('root', 'app2 --child'), ]), ])) # This passes if the MatchesPsTree matcher does not match. assert_that(pst, Not(MatchesPsTree('root', 'tini -- app', children=[ MatchesPsTree('root', 'app --arg', children=[ MatchesPsTree('appuser', 'app --child1'), ]), MatchesPsTree('root', 'app2 --arg', children=[ MatchesPsTree('root', 'app2 --child'), MatchesPsTree('appuser', 'app --child2'), ]), ])))
def test_nested_tree_different_child(self): """ MatchesPsTree can detect a child process that is different. """ ps_tree = PsTree(PsRow(1, 0, 'root', 'tini -- app'), [ PsTree(PsRow(2, 1, 'root', 'app --arg'), [ PsTree(PsRow(3, 2, 'appuser', 'app --child1')), PsTree(PsRow(4, 2, 'appuser', 'app --child3')), ]), PsTree(PsRow(5, 1, 'root', 'app2 --arg'), [ PsTree(PsRow(6, 5, 'root', 'app2 --child')), ]), ]) matcher = MatchesPsTree('root', 'tini -- app', pid=1, children=[ MatchesPsTree('root', 'app --arg', children=[ MatchesPsTree('appuser', 'app --child1'), MatchesPsTree('appuser', 'app --child2'), ]), MatchesPsTree('root', 'app2 --arg', children=[ MatchesPsTree('root', 'app2 --child'), ]), ]) mm = matcher.match(ps_tree).describe() assert "mismatches in children:" in mm assert "'app --child3' != 'app --child2': args" in mm
def test_header_alignment(self): """ Different ps implementations use different alignments for the column headers. """ ps_rows = [PsRow(1, 0, 'root', 'sleep 2')] busybox_con = FakePsContainer([ b'PID PPID RUSER COMMAND', b' 1 0 root sleep 2', b' 6 0 root ps ax -o pid,ppid,ruser,args', ]) self.assertEqual(ps_rows, list_container_processes(busybox_con)) debian_con = FakePsContainer([ b' PID PPID RUSER COMMAND', b' 1 0 root sleep 2', b' 6 0 root ps ax -o pid,ppid,ruser,args', ]) self.assertEqual(ps_rows, list_container_processes(debian_con))
def test_nested_tree_matches(self): """ MatchesPsTree can match a multi-process tree. """ ps_tree = PsTree(PsRow(1, 0, 'root', 'tini -- app'), [ PsTree(PsRow(2, 1, 'root', 'app --arg'), [ PsTree(PsRow(3, 2, 'appuser', 'app --child1')), PsTree(PsRow(4, 2, 'appuser', 'app --child2')), ]), PsTree(PsRow(5, 1, 'root', 'app2 --arg'), [ PsTree(PsRow(6, 5, 'root', 'app2 --child')), ]), ]) # Check children in the same order. matcher = MatchesPsTree('root', 'tini -- app', pid=1, children=[ MatchesPsTree('root', 'app --arg', children=[ MatchesPsTree('appuser', 'app --child1'), MatchesPsTree('appuser', 'app --child2'), ]), MatchesPsTree('root', 'app2 --arg', children=[ MatchesPsTree('root', 'app2 --child'), ]), ]) assert matcher.match(ps_tree) is None # Check children in a different order. matcher = MatchesPsTree('root', 'tini -- app', pid=1, children=[ MatchesPsTree('root', 'app --arg', children=[ MatchesPsTree('appuser', 'app --child2'), MatchesPsTree('appuser', 'app --child1'), ]), MatchesPsTree('root', 'app2 --arg', children=[ MatchesPsTree('root', 'app2 --child'), ]), ]) assert matcher.match(ps_tree) is None # Check different children in a different order. matcher = MatchesPsTree('root', 'tini -- app', pid=1, children=[ MatchesPsTree('root', 'app2 --arg', children=[ MatchesPsTree('root', 'app2 --child'), ]), MatchesPsTree('root', 'app --arg', children=[ MatchesPsTree('appuser', 'app --child1'), MatchesPsTree('appuser', 'app --child2'), ]), ]) assert matcher.match(ps_tree) is None
def test_minimal_tree_matches(self): """ MatchesPsTree can match a single-process tree. """ ps_tree = PsTree(PsRow(1, 0, 'root', 'tini -- echo "hi"')) matcher = MatchesPsTree('root', 'tini -- echo "hi"') assert matcher.match(ps_tree) is None matcher = MatchesPsTree('root', 'tini -- echo "hi"', 1) assert matcher.match(ps_tree) is None matcher = MatchesPsTree('root', 'tini -- echo "hi"', ppid=0) assert matcher.match(ps_tree) is None matcher = MatchesPsTree('root', 'tini -- echo "hi"', 1, 0) assert matcher.match(ps_tree) is None matcher = MatchesPsTree('root', 'tini -- echo "hi"', 1, 0, children=[]) assert matcher.match(ps_tree) is None
def test_minimal_tree_mismatches(self): """ MatchesPsTree can detect a non-matching single-process tree. """ ps_tree = PsTree(PsRow(1, 0, 'root', 'tini -- true')) matcher = MatchesPsTree('tuber', 'tini -- true') mismatch = matcher.match(ps_tree) assert "'root' != 'tuber': ruser" in mismatch.describe() matcher = MatchesPsTree('root', 'tini -- false') mismatch = matcher.match(ps_tree) assert "'tini -- true' != 'tini -- false': args" in mismatch.describe() matcher = MatchesPsTree('tuber', 'tini -- true', pid=7) mismatch = matcher.match(ps_tree) assert "1 != 7: pid" in mismatch.describe() matcher = MatchesPsTree('tuber', 'tini -- true', ppid=7) mismatch = matcher.match(ps_tree) assert "0 != 7: ppid" in mismatch.describe()
def test_columns(self): """ The PsRow class knows what columns it requires from ps. """ self.assertEqual(PsRow.columns(), ['pid', 'ppid', 'ruser', 'args'])
def mkrow(pid, ppid, ruser='******', args=None): if args is None: args = 'args for pid {}'.format(pid) return PsRow(pid, ppid, ruser, args)