esrally/utils/repo.py (103 lines of code) (raw):
# Licensed to Elasticsearch B.V. under one or more contributor
# license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright
# ownership. Elasticsearch B.V. licenses this file to you under
# the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import logging
import os
import sys
from esrally import exceptions
from esrally.utils import console, git, io, versions
class RallyRepository:
"""
Manages Rally resources (e.g. teams or tracks).
"""
def __init__(self, remote_url, root_dir, repo_name, resource_name, offline, fetch=True):
# If no URL is found, we consider this a local only repo (but still require that it is a git repo)
self.url = remote_url
self.remote = self.url is not None and self.url.strip() != ""
self.repo_dir = os.path.join(root_dir, repo_name)
self.resource_name = resource_name
self.offline = offline
self.logger = logging.getLogger(__name__)
self.revision = None
if self.remote and not self.offline and fetch:
# a normal git repo with a remote
if not git.is_working_copy(self.repo_dir):
git.clone(src=self.repo_dir, remote=self.url)
else:
try:
git.fetch(src=self.repo_dir, remote="origin")
except exceptions.SupplyError as e:
console.warn(
"Could not update %s. Continuing with your locally available state. Original error: %s\n"
% (self.resource_name, e.message)
)
else:
if not git.is_working_copy(self.repo_dir):
if io.exists(self.repo_dir):
raise exceptions.SystemSetupError(
"[{src}] must be a git repository.\n\nPlease run:\ngit -C {src} init".format(src=self.repo_dir)
)
raise exceptions.SystemSetupError(f"Expected a git repository at [{self.repo_dir}] but the directory does not exist.")
def update(self, distribution_version):
try:
if self.remote:
branch = versions.best_match(git.branches(self.repo_dir, remote=self.remote), distribution_version)
if branch:
# Allow uncommitted changes iff we do not have to change the branch
self.logger.info(
"Checking out [%s] in [%s] for distribution version [%s].", branch, self.repo_dir, distribution_version
)
git.checkout(self.repo_dir, branch=branch)
self.logger.info("Rebasing on [%s] in [%s] for distribution version [%s].", branch, self.repo_dir, distribution_version)
try:
git.rebase(self.repo_dir, remote="origin", branch=branch)
self.revision = git.head_revision(self.repo_dir)
except exceptions.SupplyError:
self.logger.exception("Cannot rebase due to local changes in [%s]", self.repo_dir)
console.warn(
"Local changes in [%s] prevent %s update from remote. Please commit your changes."
% (self.repo_dir, self.resource_name)
)
return
else:
msg = "Could not find %s remotely for distribution version [%s]. Trying to find %s locally." % (
self.resource_name,
distribution_version,
self.resource_name,
)
self.logger.warning(msg)
branch = versions.best_match(git.branches(self.repo_dir, remote=False), distribution_version)
if branch:
if git.current_branch(self.repo_dir) != branch:
self.logger.info(
"Checking out [%s] in [%s] for distribution version [%s].", branch, self.repo_dir, distribution_version
)
git.checkout(self.repo_dir, branch=branch)
self.revision = git.head_revision(self.repo_dir)
else:
self.logger.info(
"No local branch found for distribution version [%s] in [%s]. Checking tags.", distribution_version, self.repo_dir
)
tag = self._find_matching_tag(distribution_version)
if tag:
self.logger.info(
"Checking out tag [%s] in [%s] for distribution version [%s].", tag, self.repo_dir, distribution_version
)
git.checkout(self.repo_dir, branch=tag)
self.revision = git.head_revision(self.repo_dir)
else:
raise exceptions.SystemSetupError(
"Cannot find %s for distribution version %s" % (self.resource_name, distribution_version)
)
except exceptions.SupplyError as e:
tb = sys.exc_info()[2]
raise exceptions.DataError("Cannot update %s in [%s] (%s)." % (self.resource_name, self.repo_dir, e.message)).with_traceback(tb)
def _find_matching_tag(self, distribution_version):
tags = git.tags(self.repo_dir)
for version in versions.variants_of(distribution_version):
# tags have a "v" prefix by convention.
tag_candidate = f"v{version}"
if tag_candidate in tags:
return tag_candidate
return None
def checkout(self, revision):
self.logger.info("Checking out revision [%s] in [%s].", revision, self.repo_dir)
git.checkout(self.repo_dir, branch=revision)
def correct_revision(self, revision):
if git.is_branch(self.repo_dir, revision):
current_branch = git.current_branch(self.repo_dir)
self.logger.info("Checking current branch [%s] is equal to specified branch [%s].", current_branch, revision)
return current_branch == revision
current_revision = git.head_revision(self.repo_dir)
self.logger.info("Checking current revision [%s] is equal to specified revision [%s].", current_revision, revision)
return current_revision == revision