def restore(identity):
    config = load_config()

    s3 = boto3.resource('s3')
    bucket = s3.Bucket(config['s3']['bucket'])
    prefix = compute_top_prefix(config, identity)

    i = get_sstables_to_download(config, bucket, prefix)
    sstables = list(compute_cf_dirs(LocalPath('mirrored'), i))

    create_metadata_directories(sstables)
    instructions = get_download_instructions(identity, bucket, prefix,
                                             sstables)

    # It is assumed we'll be disk-bound, so I've chosen a typical disk queue depth.
    with futures.ThreadPoolExecutor(max_workers=32) as executor:
        fs = [
            executor.submit(download_sstable_to_path, config, *i)
            for i in instructions
        ]
        for f in futures.as_completed(fs):
            try:
                # Raise any exceptions from the executor
                f.result()
            except:
                # If there is an exception, cancel the inflight futures
                # No point throwing good work after bad
                for inflight in fs:
                    inflight.cancel()
                raise

    with MovingTemporaryDirectory(LocalPath('data')) as d:
        copy_back(LocalPath('mirrored'), d.path)
        d.finalize()
Esempio n. 2
0
def get_file_from_cache_or_s3(bucket, fn, dst, cache=True):
    hostname, _, _ = socket.gethostname().partition('.')
    look_dirs = get_property('s3_cache', hostname)
    if cache:
        cache_dir = look_dirs[-1:]
    else:
        cache_dir = []

    for d in look_dirs:
        path = (LocalPath(d) / bucket / fn)
        if path.exists():
            mkdir_p(dst)
            path.copy(dst)
            break
    else:
        b = s3.lookup(bucket)
        key = b.lookup(fn)
        if key is None:
            raise NoFileOnS3(
                "Key missing from bucket {bucket}: {key_name}".format(
                    bucket=bucket, key_name=fn))
        mkdir_p(dst)
        with open(dst, 'w') as out_f:
            out_f.write(key.read())

        for d in cache_dir:
            path = (LocalPath(d) / bucket / fn)
            if not path.exists():
                mkdir_p(path)
                LocalPath(dst).copy(path)
Esempio n. 3
0
def setup_temp_dir():
    tempdir = LocalPath(mkdtemp(dir='.', prefix='test_run.'))
    (tempdir / 'logs').mkdir()
    confdir = (tempdir / 'conf')
    confdir.mkdir()

    LocalPath('../../conf/dynomite.pem').symlink(confdir / 'dynomite.pem')

    return tempdir
Esempio n. 4
0
class Config:
    rel_ini_path = LocalPath('dct.ini')

    def __init__(self):
        config = ConfigParser()
        config.read(self.rel_ini_path)
        dct = config['dct']
        if not all(dct.values()):
            log.error('please complete the settings in %s', self.rel_ini_path)
            exit(1)

        self.package = dct['package']
        self.devpi_user = dct['devpi_user']
        self.devpi_index = dct['devpi_index']

    @classmethod
    def necessary(cls, func):
        def _necessary(*args, **kwargs):
            if not cls.rel_ini_path.exists():
                log.error("no %s in '%s'" % (cls.rel_ini_path, local.cwd))
                exit(1)

            return func(*args, **kwargs)

        return _necessary
Esempio n. 5
0
def do_backup():
    logging.basicConfig(stream=sys.stderr)
    logger.setLevel(logging.DEBUG)

    config = load_config()
    data_dir = LocalPath(config.get('data_dir', '/var/lib/cassandra'))
    state_dir = config.get('state_dir')
    state_dir = (
        LocalPath(state_dir) if state_dir is not None
        else data_dir / 'mirroring'
    )
    state_dir.mkdir()

    locs = Locations(data_dir, data_dir / 'data', state_dir, state_dir / 'links')
    backup_all_sstables(config, locs)
    mark_obsoleted(locs)
    cleanup_obsoleted(locs, 0)
Esempio n. 6
0
 def _render_files(self, version):
     """only render the files for the trigger."""
     for src in LocalPath('tpl').list():
         self.__render(src,
                       PACKAGE=config.package,
                       VERSION=version,
                       DEVPI_USER=config.devpi_user,
                       DEVPI_INDEX=config.devpi_index,
                       TIMESTAMP=datetime.now().ctime())
Esempio n. 7
0
def test_local_glob_path(tmpdir):
    p = tmpdir.mkdir("a*b?c")
    p2 = tmpdir.mkdir("aanythingbxc")
    p2.join("something.txt").write("content")
    p.join("hello.txt").write("content")
    p.join("other.txt").write("content")

    pp = LocalPath(str(p))
    assert len(pp // "*.txt") == 2
Esempio n. 8
0
def warn_shebangs(template_file: LocalPath):
    for line in template_file.read().splitlines():
        if '#!/' in line:
            warn(
                f"Be warned! Template includes '{line}', "
                "which includes what pyratemp considers a template comment, "
                "to be omitted from the rendered output. "
                "\nTo fix, replace '#!' with '#@!!@!'."
            )
Esempio n. 9
0
def save_config() -> None:
    """Persist VaRA config to a yaml file."""
    if vara_cfg()["config_file"].value is None:
        config_file = ".varats.yaml"
    else:
        config_file = str(vara_cfg()["config_file"])

    vara_cfg()["config_file"] = path.abspath(config_file)
    create_missing_folders()
    vara_cfg().store(LocalPath(config_file))
Esempio n. 10
0
    def create(self, package, devpi_user='', devpi_index=''):
        parent = local.cwd / ('devpi-cloud-test-' + package)
        if parent.exists():
            log.error("%s already exists - aborting", parent)
            exit(1)

        log.info("create new cloud test in %s", parent)
        try:
            blueprint_path = LocalPath(__file__).dirname / 'blueprint'
            blueprint_path.copy(parent)
            with local.cwd(parent):
                self.__render(Config.rel_ini_path,
                              PACKAGE=package,
                              DEVPI_USER=devpi_user,
                              DEVPI_INDEX=devpi_index)
            self._init_repository(parent)
        except Exception:
            log.exception("creation failed")
            parent.delete()
            exit(1)
Esempio n. 11
0
def get_file_remote(key, session=None):
    cache = LocalPath(CACHE)
    cache.mkdir()
    local_path = cache / (key + '.tar.gz')
    if not local_path.exists():
        if not session:
            with SshMachine(FS) as rem:
                path = rem.path(DIR) / (key + '.tar.gz')
                assert path.exists()
                plumbum.path.utils.copy(path, local_path)
                assert local_path.exists()
        else:
            rem = session
            path = rem.path(DIR) / (key + '.tar.gz')
            assert path.exists()
            plumbum.path.utils.copy(path, local_path)
            assert local_path.exists()

    fileobj = open(str(local_path))
    return tarfile.open(fileobj=fileobj)
Esempio n. 12
0
def s3_files_for(r_from, r_to, projects=[], versions=[]):
    for project, version in iter_versions(projects, versions):
        key = 'results:test-methods:{project}:{version}:dev'.format(
                project=project, version=version
        )
        test_methods = r_to.lrange(key, 0, -1)
        print "{project}:{version} has {tms} methods".format(
                project=project, version=version,
                tms=len(test_methods)
        )
        for test_method in test_methods:
            c_name, _, m_name = test_method.partition('::')

            is_class_empty = []
            for tool in ["codecover", "cobertura", "jmockit"]:
                class_key = mk_key('test-classes-cvg', [tool, project, version])
                class_r = json.loads(r_from.hget(class_key, c_name))
                is_class_empty.append(is_empty(tool, class_r))

            if all(is_class_empty):
                continue

            is_method_nonempty = []
            for tool in ["codecover", "cobertura", "jmockit"]:
                method_key = mk_key('test-methods-run-cvg', [tool, project, version])
                try:
                    method_r = json.loads(r_from.hget(method_key, test_method))
                    if not is_empty(tool, method_r):
                        key = ':'.join([tool, project, str(version), test_method])
                        DIR = '/scratch/darioush/files'
                        p = LocalPath(DIR) / '{key}.tar.gz'.format(key=key)
                        assert p.exists()
                        is_method_nonempty.append(True)
                        with open(str(p)) as f:
                            s3_up = put_into_s3('cvg-files', [tool, project, version, 'dev'], test_method, f)
                    else:
                        is_method_nonempty.append(False)
                except TypeError:
                    print "--> Missing result {tool}:{project}:{version}:dev:{test}".format(tool=tool,test=test_method, project=project, version=version)
                except AssertionError:
                    print "--> Missing file {tool}:{project}:{version}:dev:{test}".format(tool=tool,test=test_method, project=project, version=version)

            if any(is_method_nonempty):
                mut_key = mk_key('test-methods-run-cvg', ['major', project, version])
                try:
                    mut_r = json.loads(r_from.hget(mut_key, test_method))
                    if not is_empty('major', mut_r):
                        key = ':'.join(['major', project, str(version), test_method])
                        DIR = '/scratch/darioush/files'
                        p = LocalPath(DIR) / '{key}.tar.gz'.format(key=key)
                        assert p.exists()
                        with open(str(p)) as f:
                            s3_up = put_into_s3('cvg-files', ['major', project, version, 'dev'], test_method, f)
                except TypeError:
                    tool = 'major'
                    print "--> Missing result {tool}:{project}:{version}:dev:{test}".format(tool=tool,test=test_method, project=project, version=version)
                except AssertionError:
                    print "--> Missing file {tool}:{project}:{version}:dev:{test}".format(tool=tool,test=test_method, project=project, version=version)
Esempio n. 13
0
def download_to_path(marker_path, s3_object, destination, provider_args,
                     encryption_context):
    context = serialize_context(encryption_context)
    logger.debug("Invoking keypipe with context %s", context)

    keypipe_partial = functools.partial(
        keypipe.unseal,
        provider_args,
        context,
    )

    gof3r_cmd = s3gof3r['get', '--no-md5', '-b', s3_object.bucket_name, '-k',
                        s3_object.key, ]

    prefix = marker_path.name + '.'

    with TemporaryDirectory(prefix=prefix, dir=destination.up()) as d:
        temp_destination = LocalPath(d)
        start_time = time.time()
        (gof3r_cmd / keypipe_partial | lz4['-d']
         | tar['-C', temp_destination, '-x']) & FG
        finish_time = time.time()
        elapsed = finish_time - start_time

        max_mtime = max(f.stat().st_mtime_ns for f in temp_destination)
        size = sum(f.stat().st_size for f in temp_destination)

        speed = size / elapsed / 1024
        logger.info(
            'Downloaded from %s. %s bytes in %.3f seconds: %s KB/s',
            s3_object.key,
            size,
            elapsed,
            "{:,.2f}".format(speed),
        )

        timed_touch(marker_path, max_mtime)
        """Invariant: marker path exists before files are moved into the
        final data_dir.

        This prevents files that we just downloaded from being re-uploaded.
        Note that, unlike when we upload, the presence of this marker file does
        not inhibit downloads.
        """

        for i in temp_destination:
            i.link(destination / i.name)
Esempio n. 14
0
def do_restore():
    parser = argparse.ArgumentParser(
        description=
        'Restore a Cassandra backup into the current working directory.')
    parser.add_argument(
        'source_identity',
        help=
        'The identity (typically UUID) of the node whose backup should be restored'
    )
    args = parser.parse_args()

    logging.basicConfig(stream=sys.stderr)
    logger.setLevel(logging.INFO)

    if LocalPath('data').exists():
        logger.info(
            'Skipping restoration because a data directory already exists')
        return

    restore(args.source_identity)
Esempio n. 15
0
def main(project, version):
    tools = ['cobertura', 'codecover', 'jmockit']
    key = "%s:%d" % (project, version)
    r = StrictRedis.from_url(get_property('redis_url'))

    rkey = ':'.join(['results', 'test-methods-agree-cvg-nonempty', key])
    tms = r.lrange(rkey, 0, -1)

    for tool in tools:
        missings = [
            tm for (fn, tm) in [('/scratch/darioush/files/%s:%s:%s.tar.gz' %
                                 (tool, key, tm), tm)
                                for tm in tms] if not LocalPath(fn).exists()
        ]
        command = {
            'redo': True,
            'cvg_tool': tool,
            'test_methods': missings,
            'project': project,
            'version': version
        }
        if missings:
            print "python main.py q cvgmeasure.cvg.test_cvg_methods -j '%s' -t 1800 -q localH --commit" % json.dumps(
                command)
Esempio n. 16
0
 def test_basename(self):
     name = LocalPath("/some/long/path/to/file.txt").basename
     self.assertTrue(isinstance(name, six.string_types))
     self.assertEqual("file.txt", str(name))
Esempio n. 17
0
 def construct_path(self, toc_entry):
     parts = str(self.path).split('-')[:-1]
     parts.append(toc_entry)
     return LocalPath('-'.join(parts))
Esempio n. 18
0
 def test_empty(self):
     with pytest.raises(TypeError):
         LocalPath()
     assert local.path() == local.path('.')
Esempio n. 19
0
class LocalPathTest(unittest.TestCase):
    def setUp(self):
        self.longpath = LocalPath("/some/long/path/to/file.txt")
    
    def test_name(self):
        name = self.longpath.name
        self.assertTrue(isinstance(name, six.string_types))
        self.assertEqual("file.txt", str(name))

    def test_dirname(self):
        name = self.longpath.dirname
        self.assertTrue(isinstance(name, LocalPath))
        self.assertEqual("/some/long/path/to", str(name).replace("\\", "/"))

    def test_indexing(self):
        p = LocalPath("/some/long/path/to/dir")
        self.assertEqual(p[:-1], LocalPath("/some/long/path/to"))
        self.assertEqual(p[1:-1], RelativePath("long/path/to".split("/")))
        self.assertEqual(p[1], "long")
        self.assertEqual(p[::2], "some path dir".split())
        self.assertEqual(p['file.txt'], LocalPath("/some/long/path/to/dir/file.txt"))
        self.assertEqual(p['subdir/file.txt'], LocalPath("/some/long/path/to/dir/subdir/file.txt"))
        self.assertEqual(p['/root/file.txt'], LocalPath("/root/file.txt"))

    def test_uri(self):
        self.assertEqual("file:///some/long/path/to/file.txt", self.longpath.as_uri())

    @skip_without_chown
    def test_chown(self):
        with local.tempdir() as dir:
            p = dir / "foo.txt"
            p.write(six.b("hello"))
            self.assertEqual(p.uid, os.getuid())
            self.assertEqual(p.gid, os.getgid())
            p.chown(p.uid.name)
            self.assertEqual(p.uid, os.getuid())

    def test_split(self):
        p = local.path("/var/log/messages")
        self.assertEqual(p.split(), ["var", "log", "messages"])

    def test_suffix(self):
        p1 = self.longpath
        p2 = local.path("file.tar.gz")
        self.assertEqual(p1.suffix, ".txt")
        self.assertEqual(p1.suffixes, [".txt"])
        self.assertEqual(p2.suffix, ".gz")
        self.assertEqual(p2.suffixes, [".tar",".gz"])
        self.assertEqual(p1.with_suffix(".tar.gz"), local.path("/some/long/path/to/file.tar.gz"))
        self.assertEqual(p2.with_suffix(".other"), local.path("file.tar.other"))
        self.assertEqual(p2.with_suffix(".other", 2), local.path("file.other"))
        self.assertEqual(p2.with_suffix(".other", 0), local.path("file.tar.gz.other"))
        self.assertEqual(p2.with_suffix(".other", None), local.path("file.other"))

    def test_newname(self):
        p1 = self.longpath
        p2 = local.path("file.tar.gz")
        self.assertEqual(p1.with_name("something.tar"), local.path("/some/long/path/to/something.tar"))
        self.assertEqual(p2.with_name("something.tar"), local.path("something.tar"))

    def test_relative_to(self):
        p = local.path("/var/log/messages")
        self.assertEqual(p.relative_to("/var/log/messages"), RelativePath([]))
        self.assertEqual(p.relative_to("/var/"), RelativePath(["log", "messages"]))
        self.assertEqual(p.relative_to("/"), RelativePath(["var", "log", "messages"]))
        self.assertEqual(p.relative_to("/var/tmp"), RelativePath(["..", "log", "messages"]))
        self.assertEqual(p.relative_to("/opt"), RelativePath(["..", "var", "log", "messages"]))
        self.assertEqual(p.relative_to("/opt/lib"), RelativePath(["..", "..", "var", "log", "messages"]))
        for src in [local.path("/var/log/messages"), local.path("/var"), local.path("/opt/lib")]:
            delta = p.relative_to(src)
            self.assertEqual(src + delta, p)

    def test_read_write(self):
        with local.tempdir() as dir:
            f = dir / "test.txt"
            text = six.b('hello world\xd7\xa9\xd7\x9c\xd7\x95\xd7\x9d').decode("utf8")
            f.write(text, "utf8")
            text2 = f.read("utf8")
            self.assertEqual(text, text2)

    def test_parts(self):
        parts = self.longpath.parts
        self.assertEqual(parts, ('/', 'some', 'long', 'path', 'to', 'file.txt'))
        
    def test_stem(self):
        self.assertEqual(self.longpath.stem, "file")
        p = local.path("/some/directory")
        self.assertEqual(p.stem, "directory")
        
    @skipIf(pathlib is None, "This test requires pathlib")
    def test_root_drive(self):
        pl_path = pathlib.Path("/some/long/path/to/file.txt").absolute()
        self.assertEqual(self.longpath.root, pl_path.root)
        self.assertEqual(self.longpath.drive, pl_path.drive)
        
        p_path = local.cwd / "somefile.txt"
        pl_path = pathlib.Path("somefile.txt").absolute()
        self.assertEqual(p_path.root, pl_path.root)
        self.assertEqual(p_path.drive, pl_path.drive)
        
    @skipIf(pathlib is None, "This test requires pathlib")
    def test_compare_pathlib(self):
        def filename_compare(name):
            p = local.path(str(name))
            pl = pathlib.Path(str(name)).absolute()
            self.assertEqual(str(p), str(pl))
            self.assertEqual(p.parts, pl.parts)
            self.assertEqual(p.exists(), pl.exists())
            self.assertEqual(p.as_uri(), pl.as_uri())
            self.assertEqual(str(p.with_suffix('.this')), str(pl.with_suffix('.this')))
            self.assertEqual(p.name, pl.name)

        filename_compare("/some/long/path/to/file.txt")
        filename_compare(local.cwd / "somefile.txt")
        filename_compare("/some/long/path/")
        filename_compare("/some/long/path")
        filename_compare(__file__)

    def test_suffix_expected(self):
        self.assertEqual(self.longpath.preferred_suffix('.tar'), self.longpath)
        self.assertEqual((local.cwd / 'this').preferred_suffix('.txt'), local.cwd / 'this.txt')
Esempio n. 20
0
 def setUp(self):
     self.longpath = LocalPath("/some/long/path/to/file.txt")
Esempio n. 21
0
from plumbum import LocalPath

PROJECT_PATH = LocalPath(__file__).dirname.up()
""":type: LocalPath"""

OUTPUT_PATH = PROJECT_PATH / '_output'
""":type: LocalPath"""
Esempio n. 22
0
import os
from plumbum import LocalPath

ABS_PATH = LocalPath(os.path.abspath(__file__)).dirname
JAR_PATH = str(LocalPath(ABS_PATH) / 'tgLister.jar')

LINES = ['line:' + T for T in ('cobertura', 'codecover', 'jmockit', 'major')]
BRANCHES = ['branch:' + T for T in ('cobertura', 'codecover', 'jmockit')]
TERMS = ['term:' + T for T in ('cobertura', 'codecover', 'jmockit')]
STATEMENTS = ['statement:' + T for T in ('codecover', )]
LOOPS = ['loop:' + T for T in ('codecover', )]
DATA = ['data:' + T for T in ('jmockit', )]
PATHS = ['path:' + T for T in ('jmockit', )]
MUTANTS = ['mutant:' + T for T in ('major', )]
MUTCVGS = ['mutcvg:' + T for T in ('major', )]
ALL_TGS = LINES + BRANCHES + STATEMENTS + LOOPS + DATA + PATHS + MUTANTS + MUTCVGS  # + TERMS
Esempio n. 23
0
class TestBase(ABC):
    """
    Base class for tests. Tests are executed as:
        - init
        - setup, consisting of the sub steps
            - setup_prepare
            - setup_start
        - _run
        - teardown

    A test can override any of these methods.
    The `_run` method must be defined by each test.

    The commandline subcommands for `setup`, `run` and `teardown` allow to run the steps separately.
    The `init` function is always called.

    Tests should write all their artifacts to the directory `self.artifacts`, which is created
    during setup.
    """
    @cli.switch("executable",
                NameExecutable,
                list=True,
                help="Paths for executables, format name:path")
    def _set_executables(self, executables):
        self.executables = {
            name: executable
            for (name, executable) in executables
        }

    container_loaders = cli.SwitchAttr(
        "container-loader",
        ContainerLoader,
        list=True,
        help="Container loader, format tag#path")

    artifacts = cli.SwitchAttr(
        "artifacts-dir",
        LocalPath,
        envname="TEST_UNDECLARED_OUTPUTS_DIR",
        default=LocalPath("/tmp/artifacts-scion"),
        help="Directory for test artifacts. " +
        "Environment variable TEST_UNDECLARED_OUTPUTS_DIR")

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._setup_prepare_failed = False

    def init(self):
        """ init is called first. The Test object can be initialized here.
        The cli parameters have already been parsed when this is called (contrasting to __init__).
        """
        pass

    def setup(self):
        try:
            self._setup_prepare_failed = False
            self.setup_prepare()
        except:  # noqa E722, we really want to handle any exception
            self._setup_prepare_failed = True
            raise
        self.setup_start()

    @abstractmethod
    def _run(self):
        """Run the actual test. Must be implemented by concrete test.
        Note: underscored name because this clashes with plumbum.cli.Application
        """
        pass

    def teardown(self):
        pass

    def setup_prepare(self):
        """Unpacks loads local docker images and generates the topology.
        """
        docker.assert_no_networks()
        self._setup_artifacts()
        self._setup_container_loaders()
        # Define where coredumps will be stored.
        print(
            cmd.docker("run", "--rm", "--privileged", "alpine", "sysctl", "-w",
                       "kernel.core_pattern=/share/coredump"))

    def setup_start(self):
        pass

    def _setup_artifacts(self):
        # Delete old artifacts, if any.
        cmd.rm("-rf", self.artifacts)
        cmd.mkdir(self.artifacts)
        print("artifacts dir: %s" % self.artifacts)

    def _setup_container_loaders(self):
        for tag, script in self.container_loaders:
            o = local[script]()
            idx = o.index("as ")
            if idx < 0:
                logger.error("extracting tag from loader script %s" % tag)
                continue
            bazel_tag = o[idx + len("as "):].strip()
            logger.info("docker tag %s %s" % (bazel_tag, tag))
            cmd.docker("tag", bazel_tag, tag)

    def get_executable(self, name: str):
        """Resolve the executable by name.

        If the executable is not in the executables mapping, the return value
        is './bin/<name>'
        """
        return self.executables.get(name, None) or local["./bin/" + name]
Esempio n. 24
0
 def test_chown(self):
     path = LocalPath("/tmp/delme.txt")
     path.delete()
     path.write('test')
     self.assertTrue('nobody' != path.owner)
     self.assertTrue('nogroup' != path.group)
     # chown group
     path.chown(group='nogroup')
     self.assertEqual('nogroup', path.group)
     self.assertTrue('nobody' != path.owner)
     # chown owner
     path.chown('nobody')
     self.assertEqual('nobody', path.owner)
     # chown both / numerical ids
     path.chown(uid=0, gid=0)
     self.assertEqual('root', path.owner)
     self.assertEqual('root', path.group)
     # recursive
     path.chown('root', recursive=True)
         # set properties
     path.owner = 'nobody'
     self.assertEqual('nobody', path.owner)
     path.group = 'nogroup'
     self.assertEqual('nogroup', path.group)
     path.delete()
Esempio n. 25
0
 def __render(path, **kwargs):
     tpl = Template(path.read(encoding='utf-8'))
     result = tpl.safe_substitute(**kwargs)
     dst = LocalPath(path.name)
     log.info("## rendered %s ##\n%s\n## -> %s ##\n", path, result, dst)
     dst.write(result, encoding='utf-8')
Esempio n. 26
0
def mkdir_p(dst):
    LocalPath(LocalPath(dst).dirname).mkdir()
Esempio n. 27
0
 def test_dirname(self):
     name = LocalPath("/some/long/path/to/file.txt").dirname
     self.assertTrue(isinstance(name, LocalPath))
     self.assertEqual("/some/long/path/to", str(name).replace("\\", "/"))
Esempio n. 28
0
 def test_issue_139(self):
     LocalPath(local.cwd)
Esempio n. 29
0
class Dct:
    """Normal usage:

    dct trigger <version>

    Advanced usage see:
    https://github.com/google/python-fire/blob/master/doc/using-cli.md
    """
    _tpl_path = LocalPath('tpl')

    def trigger(self, version):
        """Trigger tests by rendering file and pushing changes"""
        self._render_files(version)
        self._git_push(version)

    def create(self, package, devpi_user='', devpi_index=''):
        parent = local.cwd / ('devpi-cloud-test-' + package)
        if parent.exists():
            log.error("%s already exists - aborting", parent)
            exit(1)

        log.info("create new cloud test in %s", parent)
        try:
            blueprint_path = LocalPath(__file__).dirname / 'blueprint'
            blueprint_path.copy(parent)
            with local.cwd(parent):
                self.__render(Config.rel_ini_path,
                              PACKAGE=package,
                              DEVPI_USER=devpi_user,
                              DEVPI_INDEX=devpi_index)
            self._init_repository(parent)
        except Exception:
            log.exception("creation failed")
            parent.delete()
            exit(1)

    @Config.necessary
    def _render_files(self, version):
        """only render the files for the trigger."""
        for src in LocalPath('tpl').list():
            self.__render(src,
                          PACKAGE=config.package,
                          VERSION=version,
                          DEVPI_USER=config.devpi_user,
                          DEVPI_INDEX=config.devpi_index,
                          TIMESTAMP=datetime.now().ctime())

    @staticmethod
    def __render(path, **kwargs):
        tpl = Template(path.read(encoding='utf-8'))
        result = tpl.safe_substitute(**kwargs)
        dst = LocalPath(path.name)
        log.info("## rendered %s ##\n%s\n## -> %s ##\n", path, result, dst)
        dst.write(result, encoding='utf-8')

    @Config.necessary
    def _git_push(self, version):
        """only push the changes to origin"""
        self._git_commit('%s==%s' % (config.package, version))
        git('push', 'origin', 'master')
        log.info("triggered test by pushing %s", local.cwd)

    def _init_repository(self, parent):
        with local.cwd(parent):
            git('init')
            self._git_commit('init')
            log.info("initialized. You can add your remote and push.")

    def _git_commit(self, msg):
        git('add', '.')
        git('commit', '-m', msg)