示例#1
0
文件: executable.py 项目: ckalina/skt
def cmd_merge(args):
    """
    Fetch a kernel repository, checkout particular references, and optionally
    apply patches from patchwork instances.

    Args:
        args:    Command line arguments
    """
    global retcode
    # Counter merge patch following:
    # idx[0]: counter of merge-ref option.
    # idx[1]: counter of patch option.
    # idx[2]: counter of pw option.
    idx = [0, 0, 0]

    # Clone the kernel tree and check out the proper ref.
    ktree = KernelTree(
        args.get('baserepo'),
        ref=args.get('ref'),
        wdir=full_path(args.get('workdir')),
        fetch_depth=args.get('fetch_depth')
    )
    bhead = ktree.checkout()

    # Gather the subject and date of the commit that is currently checked out.
    bsubject = ktree.get_commit_subject(bhead)
    commitdate = ktree.get_commit_date(bhead)

    # Update the state file with what we know so far.
    state = {
        'baserepo': args.get('baserepo'),
        'basehead': bhead,
        'basesubject': bsubject,
        'commitdate': commitdate,
        'workdir': full_path(args.get('workdir')),
        'tag': get_tag(full_path(args.get('workdir'))),
    }
    update_state(args['rc'], state)

    # Loop over what we have been asked to merge (if applicable).
    for thing_to_merge in args.get('merge_queue', []):
        try:
            if thing_to_merge[0] == 'merge_ref':
                mbranch_ref = thing_to_merge[1].split()

                # Update the state file with our merge_ref data.
                state = {
                    'mergerepo_%02d' % idx[0]: mbranch_ref[0],
                    'mergehead_%02d' % idx[0]: bhead
                }
                update_state(args['rc'], state)

                # Merge the git ref.
                (retcode, bhead) = ktree.merge_git_ref(*mbranch_ref)

                if retcode:
                    return

                # Increment the counter.
                idx[0] += 1

            else:
                # Attempt to merge a local patch.
                if thing_to_merge[0] == 'patch':
                    # Get the full path to the patch to merge.
                    patch = os.path.abspath(thing_to_merge[1])

                    # Update the state file with our local patch data.
                    state = {'localpatch_%02d' % idx[1]: patch}
                    update_state(args['rc'], state)

                    # Merge the patch.
                    ktree.merge_patch_file(patch)

                    # Increment the counter.
                    idx[1] += 1

                # Attempt to merge a patch from patchwork.
                elif thing_to_merge[0] == 'pw':
                    patch = thing_to_merge[1]

                    # Update the state file with our patchwork patch data.
                    state = {'patchwork_%02d' % idx[2]: patch}
                    update_state(args['rc'], state)

                    # Merge the patch. Retrieve the Patchwork session cookie
                    # first.
                    session_id = get_state(args['rc'],
                                           'patchwork_session_cookie')
                    ktree.merge_patchwork_patch(patch, session_id)

                    # Increment the counter.
                    idx[2] += 1

        # If the patch application failed, we should set the return code,
        # log an error, and update our state file.
        except PatchApplicationError as patch_exc:
            retcode = SKT_FAIL
            logging.error(patch_exc)

            # Update the state.
            state = {'mergelog': ktree.mergelog}
            update_state(args['rc'], state)

            return

        # If something else unexpected happened, re-raise the exception.
        except Exception:
            (exc, exc_type, trace) = sys.exc_info()
            raise exc, exc_type, trace

    # Get the SHA and subject of the repo after applying patches.
    buildhead = ktree.get_commit_hash()
    buildsubject = ktree.get_commit_subject()

    # Update the state file with the data about the current repo commit.
    state = {
        'buildhead': buildhead,
        'buildsubject': buildsubject
    }
    update_state(args['rc'], state)
示例#2
0
def cmd_merge(cfg):
    """
    Fetch a kernel repository, checkout particular references, and optionally
    apply patches from patchwork instances.

    Args:
        cfg:    A dictionary of skt configuration.
    """
    global retcode
    utypes = []
    ktree = KernelTree(cfg.get('baserepo'),
                       ref=cfg.get('ref'),
                       wdir=cfg.get('workdir'),
                       fetch_depth=cfg.get('fetch_depth'))
    bhead = ktree.checkout()
    commitdate = ktree.get_commit_date(bhead)
    save_state(
        cfg, {
            'baserepo': cfg.get('baserepo'),
            'basehead': bhead,
            'commitdate': commitdate
        })

    try:
        idx = 0
        for mb in cfg.get('merge_ref'):
            save_state(cfg, {
                'mergerepo_%02d' % idx: mb[0],
                'mergehead_%02d' % idx: bhead
            })
            (retcode, _) = ktree.merge_git_ref(*mb)

            utypes.append("[git]")
            idx += 1
            if retcode:
                return

        if cfg.get('patchlist'):
            utypes.append("[local patch]")
            idx = 0
            for patch in cfg.get('patchlist'):
                save_state(cfg, {'localpatch_%02d' % idx: patch})
                ktree.merge_patch_file(os.path.abspath(patch))
                idx += 1

        if cfg.get('pw'):
            utypes.append("[patchwork]")
            idx = 0
            for patch in cfg.get('pw'):
                save_state(cfg, {'patchwork_%02d' % idx: patch})
                ktree.merge_patchwork_patch(patch)
                idx += 1
    except Exception as e:
        save_state(cfg, {'mergelog': ktree.mergelog})
        raise e

    uid = "[baseline]"
    if utypes:
        uid = " ".join(utypes)

    kpath = ktree.getpath()
    buildinfo = ktree.dumpinfo()
    buildhead = ktree.get_commit_hash()

    save_state(
        cfg, {
            'workdir': kpath,
            'buildinfo': buildinfo,
            'buildhead': buildhead,
            'uid': uid
        })
示例#3
0
class KernelTreeTest(unittest.TestCase):
    # (Too many public methods) pylint: disable=too-many-public-methods
    """Test cases for KernelBuilder class."""
    def setUp(self):
        """Fixtures for testing KernelTree."""
        self.tmpdir = tempfile.mkdtemp()

        # Mock a successful subprocess.Popen
        self.m_popen_good = Mock()
        self.m_popen_good.returncode = 0
        self.m_popen_good.communicate = Mock(return_value=('stdout', 'stderr'))
        self.popen_good = mock.patch('subprocess.Popen',
                                     Mock(return_value=self.m_popen_good))

        # Mock an unsuccessful subprocess.Popen
        self.m_popen_bad = Mock()
        self.m_popen_bad.returncode = 1
        self.m_popen_bad.communicate = Mock(return_value=('stdout', 'stderr'))
        self.popen_bad = mock.patch('subprocess.Popen',
                                    Mock(return_value=self.m_popen_bad))

        self.kerneltree = KernelTree(
            uri=('git://git.kernel.org/pub/scm/linux/kernel/git/'
                 'stable/linux-stable.git'),
            ref='master',
            wdir=self.tmpdir,
            fetch_depth='1')

    def tearDown(self):
        """Teardown steps when testing is complete."""
        # Some tests remove the work directory, so we should check for it
        # before deleting it.
        if os.path.isdir(self.tmpdir):
            shutil.rmtree(self.tmpdir)

    def test_cleanup(self):
        """Ensure cleanup() removes the workdir."""
        ktree = self.kerneltree
        ktree.cleanup()

    def test_checkout(self):
        """Ensure checkout() runs git commands to check out a ref."""
        self.m_popen_good.communicate = Mock(return_value=('stdout', None))

        mock_git_cmd = mock.patch('skt.kerneltree.KernelTree.git_cmd')

        # Test with a fetch depth
        with self.popen_good, mock_git_cmd:
            result = self.kerneltree.checkout()
            self.assertEqual("stdout", result)

        # Test without a fetch depth
        self.kerneltree.fetch_depth = None
        with self.popen_good, mock_git_cmd:
            result = self.kerneltree.checkout()
            self.assertEqual("stdout", result)

    def test_dumpinfo(self):
        """Ensure dumpinfo() can dump data in a CSV format."""
        self.kerneltree.info = [('test1', 'test2', 'test3')]
        result = self.kerneltree.dumpinfo()
        expected_filename = "{}/buildinfo.csv".format(self.tmpdir)

        # Ensure a file was made and its path was returned
        self.assertTrue(os.path.isfile(expected_filename))
        self.assertEqual(result, expected_filename)

        with open(expected_filename, 'r') as fileh:
            file_contents = fileh.read()

        # Ensure the csv file has the correct data.
        self.assertEqual("test1,test2,test3\n", file_contents)

    def test_getpath(self):
        """Ensure that getpath() returns the workdir path."""
        result = self.kerneltree.getpath()
        self.assertEqual(result, self.tmpdir)

    @mock.patch('subprocess.Popen')
    def test_get_commit_date(self, mock_popen):
        """Ensure that get_commit_date() returns an integer date."""
        # Mock up an integer response that would normally come from the
        # 'git show' command
        mock_popen.return_value.communicate = Mock(return_value=('100', None))

        # Test it with a ref
        result = self.kerneltree.get_commit_date(ref='master')
        call_args = mock_popen.call_args_list[0][0]
        self.assertIn('master', call_args[0])
        mock_popen.reset_mock()

        self.assertEqual(result, 100)

        # Test it without a ref
        result = self.kerneltree.get_commit_date()
        call_args = mock_popen.call_args_list[0][0]
        self.assertNotIn('master', call_args[0])

        self.assertEqual(result, 100)

    def test_get_commit_hash(self):
        """Ensure get_commit_hash() returns a git commit hash."""
        self.m_popen_good.communicate = Mock(return_value=('abcdef', None))

        with self.popen_good:
            result = self.kerneltree.get_commit_hash(ref='master')

        self.assertEqual(result, 'abcdef')

    def test_get_remote_url(self):
        """Ensure get_remote_url() returns a fetch url."""
        expected_stdout = "Fetch URL: http://example.com/"
        self.m_popen_good.communicate = Mock(return_value=(expected_stdout,
                                                           None))

        with self.popen_good:
            result = self.kerneltree.get_remote_url('origin')

        self.assertEqual(result, 'http://example.com/')

    @mock.patch('logging.warning')
    def test_get_remote_name(self, mock_logging):
        """
        Ensure get_remote_name() handles remote names from get_remote_url().
        """
        # If get_remote_url keeps returning the same value, then
        # get_remote_name() will keep adding underscores forever and this test
        # would never pass.
        mocked_get_remote_url = mock.patch(
            'skt.kerneltree.KernelTree.get_remote_url',
            side_effect=['http://example.com/', "http://example2.com"])

        with mocked_get_remote_url:
            result = self.kerneltree.get_remote_name("http://example.com/")

        self.assertEqual('example.com_', result)

        mock_logging.assert_called_once()

    @mock.patch('logging.debug')
    @mock.patch('subprocess.check_output')
    def test_git_cmd(self, mock_check_output, mock_logging):
        """Ensure git_cmd() works."""
        mock_check_output.return_value = "Test return value"
        output = self.kerneltree.git_cmd("status")
        self.assertEqual("Test return value", output)
        mock_logging.assert_called_once()

    def test_merge_git_ref(self):
        """Ensure merge_git_ref() returns a proper tuple."""
        mock_git_cmd = mock.patch('skt.kerneltree.KernelTree.git_cmd')
        mock_get_commit_hash = mock.patch(
            'skt.kerneltree.KernelTree.get_commit_hash', return_value="abcdef")

        with mock_git_cmd, mock_get_commit_hash:
            result = self.kerneltree.merge_git_ref('http://example.com')

        self.assertTupleEqual((0, 'abcdef'), result)

    @mock.patch('logging.warning')
    @mock.patch('skt.kerneltree.KernelTree.get_remote_name')
    @mock.patch('skt.kerneltree.KernelTree.get_commit_hash')
    @mock.patch('skt.kerneltree.KernelTree.git_cmd')
    def test_merge_git_ref_failure(self, mock_git_cmd, mock_get_commit_hash,
                                   mock_get_remote_name, mock_logging):
        """Ensure merge_git_ref() fails properly when remote add fails."""
        mock_get_remote_name.return_value = "origin"
        mock_git_cmd.side_effect = [
            subprocess.CalledProcessError(1, 'That failed', "output"), True,
            subprocess.CalledProcessError(1, 'That failed', "output"), True
        ]
        mock_get_commit_hash.return_value = "abcdef"

        result = self.kerneltree.merge_git_ref('http://example.com')

        self.assertTupleEqual((1, None), result)

        mock_logging.assert_called_once()

    def test_merge_pw_patch(self):
        """Ensure merge_patchwork_patch() handles patches properly."""
        mock_gpm = mock.patch('skt.get_patch_mbox')
        mock_git_cmd = mock.patch('skt.kerneltree.KernelTree.git_cmd')
        mock_gpn = mock.patch('skt.get_patch_name', return_value="patch_name")

        self.m_popen_good.communicate = Mock(return_value=('stdout', None))
        self.m_popen_good.wait = Mock(return_value=0)

        with mock_gpm, mock_git_cmd, mock_gpn, self.popen_good:
            result = self.kerneltree.merge_patchwork_patch('uri')

        self.assertIsNone(result)
        self.assertTupleEqual(('patchwork', 'uri', 'patch_name'),
                              self.kerneltree.info[0])

    def test_merge_pw_patch_failure(self):
        """Ensure merge_patchwork_patch() handles patch failures properly."""
        mock_get_patch_mbox = mock.patch('skt.get_patch_mbox')
        mock_git_cmd = mock.patch('skt.kerneltree.KernelTree.git_cmd')

        self.m_popen_bad.communicate = Mock(return_value=('stdout', None))

        with mock_get_patch_mbox, mock_git_cmd, self.popen_bad:
            with self.assertRaises(Exception):
                self.kerneltree.merge_patchwork_patch('uri')

    def test_merge_patch_file(self):
        """Ensure merge_patch_file() tries to merge a patch."""
        mock_check_output = mock.patch('subprocess.check_output',
                                       Mock(return_value='toot'))
        patch_file = "{}/test_patch.patch".format(self.tmpdir)
        with open(patch_file, 'w') as fileh:
            fileh.write('dummy patch data')

        with mock_check_output:
            self.kerneltree.merge_patch_file(patch_file)

        self.assertTupleEqual(('patch', patch_file), self.kerneltree.info[0])

    def test_merge_patch_file_failure(self):
        """Ensure merge_patch_file() handles a patch apply failure."""
        mock_check_output = mock.patch('subprocess.check_output',
                                       side_effect=make_process_exception)

        patch_file = "{}/test_patch.patch".format(self.tmpdir)
        with open(patch_file, 'w') as fileh:
            fileh.write('dummy patch data')

        with mock_check_output:
            with self.assertRaises(Exception):
                self.kerneltree.merge_patch_file(patch_file)

    def test_merge_patch_file_missing(self):
        """Ensure merge_patch_file() fails when a patch is missing."""
        with self.assertRaises(Exception):
            self.kerneltree.merge_patch_file('patch_does_not_exist')

    @mock.patch('skt.kerneltree.KernelTree.git_cmd')
    def test_setup_repository_add(self, mock_git_cmd):
        """Ensure setup_repository() adds the origin URL."""
        mock_git_cmd.side_effect = [True, "remote1\nremote2\remote3\n", True]
        self.kerneltree.setup_repository()
        self.assertIn('add', mock_git_cmd.call_args_list[2][0])

    @mock.patch('skt.kerneltree.KernelTree.git_cmd')
    def test_setup_repository_set(self, mock_git_cmd):
        """Ensure setup_repository() sets the origin URL when it exists."""
        mock_git_cmd.side_effect = [True, "remote1\nremote2\norigin\n", True]
        self.kerneltree.setup_repository()
        self.assertIn('set-url', mock_git_cmd.call_args_list[2][0])