Esempio n. 1
0
def _upgrade_p4gf(p4):
    """Perform upgrade from earlier versions of P4GF.

    This should be invoked using _maybe_perform_init() to avoid race conditions
    across hosts.
    """
    # If updating from 12.2 to 13.1 we need to create global config file
    # (this does nothing if file already exists)

    p4gf_config.GlobalConfig.init(p4)
    with p4.at_exception_level(p4.RAISE_ERROR):
        if p4gf_config.GlobalConfig.write_if(p4):
            _info(_("Global config file '{path}' created/updated.")
                  .format(path=p4gf_config.depot_path_global()))
        else:
            _info(_("Global config file '{path}' already exists.")
                  .format(path=p4gf_config.depot_path_global()))
    # Ensure the time zone name has been set, else default to something sensible.
    tzname = P4Key.get(p4, p4gf_const.P4GF_P4KEY_TIME_ZONE_NAME)
    if tzname == "0" or tzname is None:
        msg = _("p4 key '{key}' not set, using UTC as default."
                " Change this to your Perforce server's time zone.") \
            .format(key=p4gf_const.P4GF_P4KEY_TIME_ZONE_NAME)
        LOG.warning(msg)
        sys.stderr.write(_('Git Fusion: {message}\n').format(message=msg))
        tzname = None
    else:
        # Sanity check the time zone name.
        try:
            pytz.timezone(tzname)
        except pytz.exceptions.UnknownTimeZoneError:
            LOG.warning("Time zone name '{}' unrecognized, using UTC as default".format(tzname))
            tzname = None
    if tzname is None:
        P4Key.set(p4, p4gf_const.P4GF_P4KEY_TIME_ZONE_NAME, 'UTC')
Esempio n. 2
0
 def do_acquire(self):
     """Acquire the lock and set the owner key if successful."""
     if not super(ReviewsLock, self).do_acquire():
         return False
     val = json.dumps(self.content)
     P4Key.set(self.p4, p4gf_const.P4GF_REVIEWS_COMMON_LOCK_OWNER, val)
     return True
def _prevent_access(p4):
    """Prevent further access to Git Fusion while deleting everything.

    Return the previous value of the p4key so it can be restored later.
    """
    old_value = P4Key.get(p4, p4gf_const.P4GF_P4KEY_PREVENT_NEW_SESSIONS)
    P4Key.set(p4, p4gf_const.P4GF_P4KEY_PREVENT_NEW_SESSIONS, 'true')
    return old_value
Esempio n. 4
0
def set_proxy_protects_key():
    """Get server's dm.proxy_protects and store as a key for Git Fusion."""
    v = p4gf_util.first_value_for_key(
        p4.run('configure', 'show', CONFIGURABLE_PROXY_PROTECTS),
        KEY_VALUE)
    v = 'false' if v == '0' else 'true'
    P4Key.set(p4, p4gf_const.P4GF_P4KEY_PROXY_PROTECTS, v)
    Verbosity.report(
        Verbosity.INFO, _("Configurable '{configurable}' is set to {value}.")
        .format(configurable=CONFIGURABLE_PROXY_PROTECTS, value=v))
Esempio n. 5
0
 def _write_p4key(self, change_num, val_dict):
     """Convert a dict of depot_path#rev ==> sha1 to a single string,
     write that to this changelist's p4key.
     """
     val = _format_p4key_val(val_dict)
     if len(val) > 1024:
         # For sufficiently large values, compress and base64 encode to
         # save space (about 40% at 1kb, and generally increasing with
         # input size).
         val = binascii.b2a_base64(zlib.compress(val.encode()))
     P4Key.set(self.ctx, self.p4key_name(change_num), val)
Esempio n. 6
0
def create_default_perm(p4, perm=DEFAULT_PERM):
    """Create the 'stick all users into this pull/push permission group' default p4key.

    If p4key already exists with non-zero value, leave it unchanged.
    """
    p4key = P4Key.get(p4, p4gf_const.P4GF_P4KEY_PERMISSION_GROUP_DEFAULT)
    if p4key is not None and p4key != '0':
        # Somebody already set it.
        return False
    P4Key.set(p4, p4gf_const.P4GF_P4KEY_PERMISSION_GROUP_DEFAULT, perm)
    return True
Esempio n. 7
0
def update_last_change_num(p4, change_num):
    """Update the last change number with which we synced the keys to the
    SSH configuration file. The change_num must be a positive number.
    """
    fmt = _("invalid change number '{change_num}'")
    try:
        v = int(change_num)
        if v < 1:
            raise RuntimeError(fmt.format(change_num=change_num))
        P4Key.set(p4, _get_p4key_name(), v)
    except ValueError:
        raise RuntimeError(fmt.format(change_num=change_num))
Esempio n. 8
0
 def update_last_change_num(ctx, commit_ot):
     """Update p4 key that tracks the last change_num on a branch."""
     # unconditionally add a p4key mapping change_num -> commit sha1
     ObjectType.write_index_p4key(ctx, commit_ot)
     branch_id = commit_ot.branch_id
     # only update last change_num p4key if this commit has a higher change_num
     if branch_id in ObjectType.last_commits_cache and\
             (int(ObjectType.last_commits_cache[branch_id].split(',')[0]) >
              int(commit_ot.change_num)):
         return
     (key_pattern, value) = commit_ot.to_index_last_key_value()
     P4Key.set(ctx.p4gf, key_pattern, value)
     ObjectType.last_commits_cache[branch_id] = value
def create_p4_temp_client(port=None, user=None, skip_count=False):
    """Create a connected P4 instance with a generic temporary client.

    Dropping the connection will automatically delete the client. This is
    useful for owning the locks and permitting reliable lock stealing by
    other processes.

    :return: P4API instance.

    """
    p4 = create_p4(port, user, warn_no_client=False)
    if p4 is None:
        # Propagate the error (that has already been reported).
        return None
    name = p4gf_const.P4GF_OBJECT_CLIENT_UNIQUE.format(
        server_id=p4gf_util.get_server_id(), uuid=str(uuid.uuid1()))
    client = p4.fetch_client(name)
    client['Owner'] = p4gf_const.P4GF_USER
    client['LineEnd'] = NTR('unix')
    client['View'] = [
        '//{0}/... //{1}/...'.format(p4gf_const.P4GF_DEPOT, name)
    ]
    # to prevent the mirrored git commit/tree objects from being retained in the
    # git-fusion workspace, set client option 'rmdir' and sync #none in p4gf_gitmirror
    client['Options'] = p4gf_const.CLIENT_OPTIONS.replace("normdir", "rmdir")
    client['Root'] = p4gf_const.P4GF_HOME
    # The -x option is a deep undoc feature that signals to p4d that this
    # is a temporary client, which will be automatically deleted upon
    # disconnect. Requires passing the client specification using -i flag.
    # N.B. this client cannot shelve changes. See @465851 for details.
    # N.B. only one temporary client per connection will be auto-deleted
    p4.client = name
    p4.save_client(client, '-x')
    LOG.debug("create_p4_temp_client() created temp client %s", name)
    if 'P4T4TEST_ORIG_LANG' in os.environ and not skip_count:
        # In the testing environment, we check that each process created no
        # more than one temporary client (concurrently). Keep the highest
        # value, rather than whatever the last count happened to be.
        prefix = p4gf_const.P4GF_OBJECT_CLIENT_UNIQUE.format(
            server_id=p4gf_util.get_server_id(), uuid='')
        count = _count_active(prefix)
        # Some tests set up an unnatural environment, so don't let those
        # blow up in unexpected ways (i.e. fail gracefully elsewhere).
        with p4.at_exception_level(P4.P4.RAISE_NONE):
            value = p4gf_p4key.get(
                p4, "git-fusion-temp-clients-{}".format(os.getpid()))
            if int(value) < count:
                p4gf_p4key.set(
                    p4, "git-fusion-temp-clients-{}".format(os.getpid()),
                    str(count))
    return p4
Esempio n. 10
0
def overwrite_last_copied_tag(p4, repo_name, change_num):
    """Set the changelist number for the most recent tags change.

    :type p4: :class:`P4API`
    :param p4: P4 API instance

    :type repo_name: str
    :param repo_name: name fo the Git Fusion repository

    :type change_num: int || str
    :param change_num: changelist number to assign to tags key.

    """
    keyname = _calc_last_copied_tag_p4key_name(repo_name,
                                               p4gf_util.get_server_id())
    LOG.debug('overwrite_last_copied_tag() assigning {} to {}'.format(
        change_num, keyname))
    return P4Key.set(p4, keyname, change_num)
Esempio n. 11
0
 def write_index_p4key(ctx, commit_ot):
     """Record the p4key index that goes with this commit."""
     (key_name, value) = commit_ot.to_index_key_value()
     P4Key.set(ctx.p4gf, key_name, value)
def delete_all(args, p4, metrics):
    """Remove all Git Fusion clients, as well as the object cache.

    Keyword arguments:
        args -- parsed command line arguments
        p4   -- Git user's Perforce client
    """
    # pylint:disable=too-many-branches
    p4.user = p4gf_const.P4GF_USER
    group_list = [p4gf_const.P4GF_GROUP_PULL, p4gf_const.P4GF_GROUP_PUSH]
    print(_('Connected to {P4PORT}').format(P4PORT=p4.port))
    print_verbose(args, _('Scanning for Git Fusion clients...'))
    client_name = p4gf_util.get_object_client_name()
    locks = _lock_all_repos(p4)
    if args.delete:
        was_prevented = _prevent_access(p4)
    else:
        was_prevented = None
    delete_clients(args, p4, metrics)
    # Retrieve the names of the initialization/upgrade "lock" p4keys.
    p4keys = [
        p4gf_const.P4GF_P4KEY_ALL_PENDING_MB,
        p4gf_const.P4GF_P4KEY_ALL_REMAINING_MB
    ]
    # Key patterns NOT published in p4gf_const because they have trailing *
    # wildcards and it's not worth cluttering p4gf_const for this one use.
    p4key_patterns = [
        'git-fusion-init-started*', 'git-fusion-init-complete*',
        'git-fusion-upgrade-started*', 'git-fusion-upgrade-complete*',
        'git-fusion-index-*'
    ]
    for p4key_pattern in p4key_patterns:
        d = P4Key.get_all(p4, p4key_pattern)
        p4keys.extend(sorted(d.keys()))
    localroot = get_p4gf_localroot(p4)
    if not args.delete:
        if localroot:
            if args.no_obliterate:
                print(NTR('p4 sync -f #none'))
            else:
                print(NTR('p4 client -f -d {}').format(client_name))
                print(NTR('rm -rf {}').format(localroot))
        if not args.no_obliterate:
            print(
                NTR('p4 obliterate -hay //{}/objects/...').format(
                    p4gf_const.P4GF_DEPOT))
        for p4key in p4keys:
            print(NTR('p4 key -d {}').format(p4key))
        for group in group_list:
            print(NTR('p4 group -a -d {}').format(group))
    else:
        if localroot:
            if not args.no_obliterate:
                # Need this in order to use --gc later on
                # client should not exist; this is likely a NOOP
                p4gf_util.p4_client_df(p4, client_name)
                metrics.clients += 1
                print_verbose(
                    args,
                    _("Deleting client '{client_name}'s workspace...").format(
                        client_name=client_name))
                _remove_local_root(localroot)
        _delete_cache(args, p4, metrics)
        print_verbose(args, _('Removing initialization p4keys...'))
        for p4key in p4keys:
            delete_p4key(p4, p4key, metrics)
        for group in group_list:
            delete_group(args, p4, group, metrics)
    _release_locks(locks)
    if was_prevented is not None:
        if was_prevented != '0':
            P4Key.set(p4, p4gf_const.P4GF_P4KEY_PREVENT_NEW_SESSIONS,
                      was_prevented)
        else:
            P4Key.delete(p4, p4gf_const.P4GF_P4KEY_PREVENT_NEW_SESSIONS)
Esempio n. 13
0
def _write_last_copied_tag(ctx, change_num):
    """Update the changelist number for the most recent tags change for this
    Git Fusion server.
    """
    return P4Key.set(ctx.p4gf, _last_copied_tag_p4key_name(ctx), change_num)
 def _set_key(self, key, value):
     """Set a key."""
     if '{repo_name}' in key:
         key = key.format(repo_name=self.repo_name)
     p4gf_p4key.set(self.p4, key, str(value))
def main():
    """Parse the command-line arguments and report on locks."""
    # pylint: disable=too-many-statements
    desc = _("Report the currently held locks in Git Fusion.")
    parser = p4gf_util.create_arg_parser(desc=desc)
    parser.add_argument('--test',
                        action='store_true',
                        help=_('invoke test mode, acquire locks and report'))
    parser.add_argument(
        '--test2',
        action='store_true',
        help=_(
            'invoke test mode, acquire locks and report, set dead processes.'))
    args = parser.parse_args()

    p4gf_util.has_server_id_or_exit()
    server_id = p4gf_util.get_server_id()
    p4 = p4gf_create_p4.create_p4_temp_client()
    if not p4:
        sys.exit(1)
    print("Connecting to P4PORT={} as P4USER={}".format(p4.port, p4.user))
    if args.test or args.test2:
        repo_name = "p4gf_test_status_repo"
        status_key_name = p4gf_p4key.calc_repo_status_p4key_name(
            repo_name, None)
        p4gf_p4key.set(p4, status_key_name, 'Push 1 completed successfully')
        pushid_key_name = p4gf_p4key.calc_repo_push_id_p4key_name(repo_name)
        p4gf_p4key.set(p4, pushid_key_name, '1')
        # create a process and kill it and set its dead pid as a RepoLock owner below.

        if args.test:
            # A test with nothing stale
            with ExitStack() as stack:
                stack.enter_context(p4gf_lock.ReviewsLock(p4))
                stack.enter_context(p4gf_lock.RepoLock(p4, repo_name))
                stack.enter_context(p4gf_git_repo_lock.read_lock(repo_name))
                stack.enter_context(
                    p4gf_git_repo_lock.write_lock(repo_name, upgrade=True))
                print_lock_status(p4, server_id)

        else:  # if args.test2
            # Now a test with some DEAD processes and a stale view Lock
            dead_process = subprocess.Popen(['echo', 'x'],
                                            stdout=subprocess.DEVNULL)
            dead_process.kill()
            while dead_process.returncode is None:
                dead_process.communicate()
            lock2 = None
            with ExitStack() as stack:
                stack.enter_context(p4gf_lock.ReviewsLock(p4))
                # first lock owner
                lock1 = p4gf_lock.RepoLock(p4, repo_name)
                # second lock owner with same group_id and a dead pid
                lock2 = p4gf_lock.RepoLock(p4,
                                           repo_name,
                                           group_id=lock1.group_id)
                lock2.process_id = dead_process.pid
                # acquire the first RepoLock
                stack.enter_context(lock1)
                # Use low level method to add this DEAD pid to the group's lock owners
                lock2.do_acquire()
                stack.enter_context(p4gf_git_repo_lock.read_lock(repo_name))
                stack.enter_context(
                    p4gf_git_repo_lock.write_lock(repo_name, upgrade=True))
                print("Test 1:")
                print_lock_status(p4, server_id)
                p4gf_p4key.set(p4, pushid_key_name, '2')
                p4gf_p4key.set(p4, status_key_name,
                               'Push 2 failed: some error')
                # Finally lets set the P4GF_P4KEY_LOCK_VIEW - the least likley to be stale
                p4gf_p4key.set(p4, lock2.lock_key, '1')
                print("Test 2:")
                print_lock_status(p4, server_id)
                # Cant exit the ExistStack unless we clean this
                p4gf_p4key.delete(p4, lock2.lock_key)
            # Clean up this lock so the test may be run again
            p4gf_p4key.delete(p4, lock2.owners_key)
        # remove test keys
        p4gf_p4key.delete(p4, status_key_name)
        p4gf_p4key.delete(p4, pushid_key_name)
    else:
        print_lock_status(p4, server_id)
Esempio n. 16
0
 def _write(self, content):
     """Write content to the key."""
     val = json.dumps(content)
     P4Key.set(self.p4, self.owners_key, val)
Esempio n. 17
0
def ensure_depot():
    """Create depot P4GF_DEPOT if not already exists."""
    # We use P4GF_P4KEY_P4GF_DEPOT to send the depot name
    # to p4gf_submit_trigger.py. Ensure that P4GF_P4KEY_P4GF_DEPOT matches
    # requested depot name from P4GF_ENV.
    # pylint: disable=line-too-long
    p4gf_depot_key = P4Key.get(p4, p4gf_const.P4GF_P4KEY_P4GF_DEPOT)
    if p4gf_depot_key != '0' and p4gf_depot_key != p4gf_const.P4GF_DEPOT:
        Verbosity.report(
            Verbosity.ERROR,
            _("\n**ERROR: There is a configuration conflict for this Git Fusion instance of the P4GF_DEPOT.\n"
              "  P4GF_DEPOT is configured to a value different than that recorded in the p4d server.\n"
              "  You must correct this as described here.\n"
              "  This key must either be:\n"
              "   1) unset, in which case this initialization will set it to the correct value, or\n"
              "   2) match the value set in the P4GF_ENV configuration file, or\n"
              "   3) set to the default '.git-fusion' if not set to a different value in the P4GF_ENV configuration file.\n"
              "\n"
              "  The p4d server p4key '{key}' is set to '{value}'.\n")
            .format(key=p4gf_const.P4GF_P4KEY_P4GF_DEPOT, value=p4gf_depot_key))
        depot_configured = (p4gf_const.P4GF_ENV and p4gf_env_config.P4GF_ENV_CONFIG_DICT and
                            p4gf_const.P4GF_DEPOT_NAME in p4gf_env_config.P4GF_ENV_CONFIG_DICT)
        if depot_configured:
            msg = _("  This Git Fusion instance is configured with Git Fusion depot = '{depot}'"
                    "\n    from P4GF_ENV file '{env}' containing these settings:\n") \
                .format(depot=p4gf_const.P4GF_DEPOT, env=p4gf_const.P4GF_ENV)
            for k, v in p4gf_env_config.P4GF_ENV_CONFIG_DICT.items():
                msg = msg + "        {0} = {1}\n".format(k, v)
        else:
            msg = _("  This Git Fusion instance is configured for the default P4GF_DEPOT : '{depot}'.\n")\
                .format(depot=p4gf_const.P4GF_DEPOT)
        Verbosity.report(Verbosity.ERROR, msg)
        Verbosity.report(
            Verbosity.ERROR,
            _("  To correct this mismatch , determine the required P4GF_DEPOT value.\n"
              "    1) If the value of p4key '{key}'='{value}' is different than the required value, remove the p4key.\n"
              "           Make certain the current p4key value was not correctly set by another Git Fusion instance.\n"
              "    2) If the required value is the default 'git-fusion', and there is a P4GF_DEPOT setting in the P4GF_ENV file, remove it.\n"
              "    2) If the required value is not the default 'git-fusion', set P4GF_DEPOT to the required value in the P4GF_ENV file.\n"
              "    3) Re-run this configure-git-fusion.sh script.")
            .format(key=p4gf_const.P4GF_P4KEY_P4GF_DEPOT, value=p4gf_depot_key))
        sys.exit(1)

    try:
        created = p4gf_p4spec.ensure_depot_gf(p4)
        if created:
            Verbosity.report(Verbosity.INFO, _("Depot '{depot}' created.")
                             .format(depot=p4gf_const.P4GF_DEPOT))
        else:
            Verbosity.report(
                Verbosity.INFO, _("Depot '{depot}' already exists. Not creating.")
                .format(depot=p4gf_const.P4GF_DEPOT))
    except Exception as depote:
        Verbosity.report(Verbosity.INFO, _("Error creating Depot '{depot}'.")
                         .format(depot=p4gf_const.P4GF_DEPOT))
        raise depote

    # Set the P4GF_DEPOT p4key to send the depot name to p4gf_submit_trigger.py.
    # This will either be the default or the value set from P4GF_ENV config file
    P4Key.set(p4, p4gf_const.P4GF_P4KEY_P4GF_DEPOT, p4gf_const.P4GF_DEPOT)
    return created
Esempio n. 18
0
def set_server_id_p4key(server_id):
    """Set the server-id p4key value to the hostname to identify GF hosts."""
    if server_id:
        P4Key.set(p4, p4gf_const.P4GF_P4KEY_SERVER_ID + server_id,
                  p4gf_util.get_hostname())