mach
author Martin Thomson <martin.thomson@gmail.com>
Wed, 03 Jan 2018 15:36:18 +1100
changeset 14254 74e4cd34e49fa72806ed7405926591d90f4a2d2a
parent 13552 16d3e6c47964e33ae3a088939acb43295196f539
child 14308 92ec52c839cb3ae890d7a9f228e3c12dc72b6f38
permissions -rwxr-xr-x
Bug 1427675 - Add TlsAgent argument to TlsRecordFilter, r=ekr This is a fairly disruptive change, but mostly just mechanical. There are a few extra changes: - I have renamed the TlsInspector* filters for consistency. This was purely mechanical. - I renamed the SetPacketFilter function to just SetFilter. Also mechanical. - TlsRecordFilter maintains a weak pointer reference to the TlsAgent now rather than using a bare pointer. This meant that I had to change TlsAgentTestBase to use shared_ptr rather than unique_ptr to support of use of filters with those tests. - I removed the helper function that enables decryption. Enabling decryption is now more explicit. - I ran a newer clang-format version and it fixed a few extra things, like the comments on the end of namespace {} blocks, some of which were wrong. - I discovered a bug in some of the drop tests: in the 0-RTT tests, the filters were being installed on the client and server right at the start, which meant that they were capturing the first handshake and not the second one. This was clearly against intent, but the tests were mostly right still, it was only the expected ACKs that were wrong. We were expecting just one record to be ACKed by a server (Finished), but the record with EndOfEarlyData should have been acknowledged as well. - In TlsSkipTest and Tls13SkipTest, I had to override SetUp() so that client_ and server_ are initialized prior to constructing filters. In doing so, I noticed that we weren't being consistent about overriding SetUp properly, so I fixed the small number of instances of that by adding an override label to each and marking the base method virtual. - The stateless HRR test for TLS 1.3 compat mode was replacing the server, but expecting to retain the same filters. That wasn't a problem in that case, but I didn't want to have any places where the filter was set on a different agent from the one that was passed to it.

#!/usr/bin/env python
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
##########################################################################
#
# This is a collection of helper tools to get stuff done in NSS.
#

import sys
import argparse
import subprocess
import os
import platform
from hashlib import sha256

cwd = os.path.dirname(os.path.abspath(__file__))


class cfAction(argparse.Action):
    docker_command = ["docker"]
    restorecon = None

    def __call__(self, parser, args, values, option_string=None):
        if not args.noroot:
            self.setDockerCommand()

        if values:
            files = [os.path.relpath(os.path.abspath(x), start=cwd) for x in values]
        else:
            files = self.modifiedFiles()
        files = [os.path.join('/home/worker/nss', x) for x in files]

        # First check if we can run docker.
        try:
            with open(os.devnull, "w") as f:
                subprocess.check_call(
                    self.docker_command + ["images"], stdout=f)
        except:
            print("Please install docker and start the docker daemon.")
            sys.exit(1)

        docker_image = 'clang-format-service:latest'
        cf_docker_folder = cwd + "/automation/clang-format"

        # Build the image if necessary.
        if self.filesChanged(cf_docker_folder):
            self.buildImage(docker_image, cf_docker_folder)

        # Check if we have the docker image.
        try:
            command = self.docker_command + [
                "image", "inspect", "clang-format-service:latest"
            ]
            with open(os.devnull, "w") as f:
                subprocess.check_call(command, stdout=f)
        except:
            print("I have to build the docker image first.")
            self.buildImage(docker_image, cf_docker_folder)

        command = self.docker_command + [
            'run', '-v', cwd + ':/home/worker/nss:Z', '--rm', '-ti', docker_image
        ]
        # The clang format script returns 1 if something's to do. We don't
        # care.
        subprocess.call(command + files)
        if self.restorecon is not None:
            subprocess.call([self.restorecon, '-R', cwd])

    def filesChanged(self, path):
        hash = sha256()
        for dirname, dirnames, files in os.walk(path):
            for file in files:
                with open(os.path.join(dirname, file), "rb") as f:
                    hash.update(f.read())
        chk_file = cwd + "/.chk"
        old_chk = ""
        new_chk = hash.hexdigest()
        if os.path.exists(chk_file):
            with open(chk_file) as f:
                old_chk = f.readline()
        if old_chk != new_chk:
            with open(chk_file, "w+") as f:
                f.write(new_chk)
            return True
        return False

    def buildImage(self, docker_image, cf_docker_folder):
        command = self.docker_command + [
            "build", "-t", docker_image, cf_docker_folder
        ]
        subprocess.check_call(command)
        return

    def setDockerCommand(self):
        if platform.system() == "Linux":
            from distutils.spawn import find_executable
            self.restorecon = find_executable('restorecon')
            self.docker_command = ["sudo"] + self.docker_command

    def modifiedFiles(self):
        files = []
        if os.path.exists(os.path.join(cwd, '.hg')):
            st = subprocess.Popen(['hg', 'status', '-m', '-a'],
                                  cwd=cwd, stdout=subprocess.PIPE)
            for line in iter(st.stdout.readline, ''):
                files += [line[2:].rstrip()]
        elif os.path.exists(os.path.join(cwd, '.git')):
            st = subprocess.Popen(['git', 'status', '--porcelain'],
                                  cwd=cwd, stdout=subprocess.PIPE)
            for line in iter(st.stdout.readline, ''):
                if line[1] == 'M' or line[1] != 'D' and \
                        (line[0] == 'M' or line[0] == 'A' or
                         line[0] == 'C' or line[0] == 'U'):
                    files += [line[3:].rstrip()]
                elif line[0] == 'R':
                    files += [line[line.index(' -> ', beg=4) + 4:]]
        else:
            print('Warning: neither mercurial nor git detected!')

        def isFormatted(x):
            return x[-2:] == '.c' or x[-3:] == '.cc' or x[-2:] == '.h'
        return [x for x in files if isFormatted(x)]


class buildAction(argparse.Action):

    def __call__(self, parser, args, values, option_string=None):
        cwd = os.path.dirname(os.path.abspath(__file__))
        subprocess.check_call([cwd + "/build.sh"] + values)


class testAction(argparse.Action):

    def runTest(self, test, cycles="standard"):
        cwd = os.path.dirname(os.path.abspath(__file__))
        domsuf = os.getenv('DOMSUF', "localdomain")
        host = os.getenv('HOST', "localhost")
        env = {
            "NSS_TESTS": test,
            "NSS_CYCLES": cycles,
            "DOMSUF": domsuf,
            "HOST": host
        }
        os_env = os.environ
        os_env.update(env)
        command = cwd + "/tests/all.sh"
        subprocess.check_call(command, env=os_env)

    def __call__(self, parser, args, values, option_string=None):
        self.runTest(values)


class commandsAction(argparse.Action):
    commands = []

    def __call__(self, parser, args, values, option_string=None):
        for c in commandsAction.commands:
            print(c)


def parse_arguments():
    parser = argparse.ArgumentParser(
        description='NSS helper script. ' +
        'Make sure to separate sub-command arguments with --.')
    subparsers = parser.add_subparsers()

    parser_build = subparsers.add_parser(
        'build', help='All arguments are passed to build.sh')
    parser_build.add_argument(
        'build_args', nargs='*', help="build arguments", action=buildAction)

    parser_cf = subparsers.add_parser(
        'clang-format',
        help="""
        Run clang-format.

        By default this runs against any files that you have modified.  If
        there are no modified files, it checks everything.
        """)
    parser_cf.add_argument(
        '--noroot',
        help='On linux, suppress the use of \'sudo\' for running docker.',
        action='store_true')
    parser_cf.add_argument(
        '<file/dir>',
        nargs='*',
        help="Specify files or directories to run clang-format on",
        action=cfAction)

    parser_test = subparsers.add_parser(
        'tests', help='Run tests through tests/all.sh.')
    tests = [
        "cipher", "lowhash", "chains", "cert", "dbtests", "tools", "fips",
        "sdr", "crmf", "smime", "ssl", "ocsp", "merge", "pkits", "ec",
        "gtests", "ssl_gtests"
    ]
    parser_test.add_argument(
        'test', choices=tests, help="Available tests", action=testAction)

    parser_commands = subparsers.add_parser(
        'mach-commands',
        help="list commands")
    parser_commands.add_argument(
        'mach-commands',
        nargs='*',
        action=commandsAction)

    commandsAction.commands = [c for c in subparsers.choices]
    return parser.parse_args()


def main():
    parse_arguments()


if __name__ == '__main__':
    main()