1# Copyright 2013 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from caching_file_system import CachingFileSystem
6from gitiles_file_system import GitilesFileSystem
7from local_file_system import LocalFileSystem
8from offline_file_system import OfflineFileSystem
9from gitiles_file_system import GitilesFileSystem
10from third_party.json_schema_compiler.memoize import memoize
11
12
13class HostFileSystemProvider(object):
14  '''Provides host file systems ("host" meaning the file system that hosts the
15  server's source code and templates) tracking master, or any branch.
16
17  File system instances are memoized to maintain the in-memory caches across
18  multiple callers.
19  '''
20  def __init__(self,
21               object_store_creator,
22               pinned_commit=None,
23               default_master_instance=None,
24               offline=False,
25               constructor_for_test=None):
26    '''
27    |object_store_creator|
28      Provides caches for file systems that need one.
29    |pinned_commit|
30      If not None, the commit at which a 'master' file system will be created.
31      If None, 'master' file systems will use HEAD.
32    |default_master_instance|
33      If not None, 'master' file systems provided by this class without a
34      specific commit will return |default_master_instance| instead.
35    |offline|
36      If True all provided file systems will be wrapped in an OfflineFileSystem.
37    |constructor_for_test|
38      Provides a custom constructor rather than creating GitilesFileSystems.
39    '''
40    self._object_store_creator = object_store_creator
41    self._pinned_commit = pinned_commit
42    self._default_master_instance = default_master_instance
43    self._offline = offline
44    self._constructor_for_test = constructor_for_test
45
46  @memoize
47  def GetMaster(self, commit=None):
48    '''Gets a file system tracking 'master'. Use this method rather than
49    GetBranch('master') because the behaviour is subtly different; 'master' can
50    be pinned to a specific commit (|pinned_commit| in constructor) and can have
51    have its default instance overridden (|default_master_instance| in the
52    constructor).
53
54    |commit| if non-None determines a specific commit to pin the host file
55    system at, though it will be ignored if it's newer than |pinned_commit|.
56    If None then |commit| will track |pinned_commit| if is has been
57    set, or just HEAD (which might change during server runtime!).
58    '''
59    if commit is None:
60      if self._default_master_instance is not None:
61        return self._default_master_instance
62      return self._Create('master', commit=self._pinned_commit)
63    if self._pinned_commit is not None:
64      # XXX(ahernandez): THIS IS WRONG. Should be
65      # commit = Oldest(commit, self._pinned_commit).
66      commit = min(commit, self._pinned_commit)
67    return self._Create('master', commit=commit)
68
69  @memoize
70  def GetBranch(self, branch):
71    '''Gets a file system tracking |branch|, for example '1150' - anything other
72    than 'master', which must be constructed via the GetMaster() method.
73
74    Note: Unlike GetMaster this function doesn't take a |commit| argument
75    since we assume that branches hardly ever change, while master frequently
76    changes.
77    '''
78    assert isinstance(branch, basestring), 'Branch %s must be a string' % branch
79    assert branch != 'master', (
80        'Cannot specify branch=\'master\', use GetMaster()')
81    return self._Create(branch)
82
83  def _Create(self, branch, commit=None):
84    '''Creates Gitiles file systems (or if in a test, potentially whatever
85    |self._constructor_for_test specifies). Wraps the resulting file system in
86    an Offline file system if the offline flag is set, and finally wraps it in
87    a Caching file system.
88    '''
89    if self._constructor_for_test is not None:
90      file_system = self._constructor_for_test(branch=branch, commit=commit)
91    else:
92      file_system = GitilesFileSystem.Create(branch=branch, commit=commit)
93    if self._offline:
94      file_system = OfflineFileSystem(file_system)
95    return CachingFileSystem(file_system, self._object_store_creator)
96
97  @staticmethod
98  def ForLocal(object_store_creator, **optargs):
99    '''Used in creating a server instance on localhost.
100    '''
101    return HostFileSystemProvider(
102        object_store_creator,
103        constructor_for_test=lambda **_: LocalFileSystem.Create(),
104        **optargs)
105
106  @staticmethod
107  def ForTest(file_system, object_store_creator, **optargs):
108    '''Used in creating a test server instance. The HostFileSystemProvider
109    returned here will always return |file_system| when its Create() method is
110    called.
111    '''
112    return HostFileSystemProvider(
113        object_store_creator,
114        constructor_for_test=lambda **_: file_system,
115        **optargs)
116