Enable Enterprise enrollment on desktop builds.
[chromium-blink-merge.git] / chrome / common / extensions / docs / server2 / content_providers.py
blobe9cfacafb01906f4c3e223c82014bbba871b807b
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.
5 import logging
6 import os
7 import traceback
9 from chroot_file_system import ChrootFileSystem
10 from content_provider import ContentProvider
11 import environment
12 from extensions_paths import CONTENT_PROVIDERS, LOCAL_DEBUG_DIR
13 from future import Future
14 from local_file_system import LocalFileSystem
15 from third_party.json_schema_compiler.memoize import memoize
18 _IGNORE_MISSING_CONTENT_PROVIDERS = [False]
21 def IgnoreMissingContentProviders(fn):
22 '''Decorates |fn| to ignore missing content providers during its run.
23 '''
24 def run(*args, **optargs):
25 saved = _IGNORE_MISSING_CONTENT_PROVIDERS[0]
26 _IGNORE_MISSING_CONTENT_PROVIDERS[0] = True
27 try:
28 return fn(*args, **optargs)
29 finally:
30 _IGNORE_MISSING_CONTENT_PROVIDERS[0] = saved
31 return run
34 class ContentProviders(object):
35 '''Implements the content_providers.json configuration; see
36 chrome/common/extensions/docs/templates/json/content_providers.json for its
37 current state and a description of the format.
39 Returns ContentProvider instances based on how they're configured there.
40 '''
42 def __init__(self,
43 object_store_creator,
44 compiled_fs_factory,
45 host_file_system,
46 github_file_system_provider,
47 gcs_file_system_provider):
48 self._object_store_creator = object_store_creator
49 self._compiled_fs_factory = compiled_fs_factory
50 self._host_file_system = host_file_system
51 self._github_file_system_provider = github_file_system_provider
52 self._gcs_file_system_provider = gcs_file_system_provider
53 self._cache = None
55 # If running the devserver and there is a LOCAL_DEBUG_DIR, we
56 # will read the content_provider configuration from there instead
57 # of fetching it from SVN trunk or patch.
58 if environment.IsDevServer() and os.path.exists(LOCAL_DEBUG_DIR):
59 local_fs = LocalFileSystem(LOCAL_DEBUG_DIR)
60 conf_stat = None
61 try:
62 conf_stat = local_fs.Stat(CONTENT_PROVIDERS)
63 except:
64 pass
66 if conf_stat:
67 logging.warn(("Using local debug folder (%s) for "
68 "content_provider.json configuration") % LOCAL_DEBUG_DIR)
69 self._cache = compiled_fs_factory.ForJson(local_fs)
71 if not self._cache:
72 self._cache = compiled_fs_factory.ForJson(host_file_system)
74 @memoize
75 def GetByName(self, name):
76 '''Gets the ContentProvider keyed by |name| in content_providers.json, or
77 None of there is no such content provider.
78 '''
79 config = self._GetConfig().get(name)
80 if config is None:
81 logging.error('No content provider found with name "%s"' % name)
82 return None
83 return self._CreateContentProvider(name, config)
85 @memoize
86 def GetByServeFrom(self, path):
87 '''Gets a (content_provider, serve_from, path_in_content_provider) tuple,
88 where content_provider is the ContentProvider with the longest "serveFrom"
89 property that is a subpath of |path|, serve_from is that property, and
90 path_in_content_provider is the remainder of |path|.
92 For example, if content provider A serves from "foo" and content provider B
93 serves from "foo/bar", GetByServeFrom("foo/bar/baz") will return (B,
94 "foo/bar", "baz").
96 Returns (None, '', |path|) if no ContentProvider serves from |path|.
97 '''
98 serve_from_to_config = dict(
99 (config['serveFrom'], (name, config))
100 for name, config in self._GetConfig().iteritems())
101 path_parts = path.split('/')
102 for i in xrange(len(path_parts), -1, -1):
103 name_and_config = serve_from_to_config.get('/'.join(path_parts[:i]))
104 if name_and_config is not None:
105 return (self._CreateContentProvider(name_and_config[0],
106 name_and_config[1]),
107 '/'.join(path_parts[:i]),
108 '/'.join(path_parts[i:]))
109 return None, '', path
111 def _GetConfig(self):
112 return self._cache.GetFromFile(CONTENT_PROVIDERS).Get()
114 def _CreateContentProvider(self, name, config):
115 default_extensions = config.get('defaultExtensions', ())
116 supports_templates = config.get('supportsTemplates', False)
117 supports_zip = config.get('supportsZip', False)
119 if 'chromium' in config:
120 chromium_config = config['chromium']
121 if 'dir' not in chromium_config:
122 logging.error('%s: "chromium" must have a "dir" property' % name)
123 return None
124 file_system = ChrootFileSystem(self._host_file_system,
125 chromium_config['dir'])
126 elif 'gcs' in config:
127 gcs_config = config['gcs']
128 if 'bucket' not in gcs_config:
129 logging.error('%s: "gcs" must have a "bucket" property' % name)
130 return None
131 bucket = gcs_config['bucket']
132 if not bucket.startswith('gs://'):
133 logging.error('%s: bucket %s should start with gs://' % (name, bucket))
134 return None
135 bucket = bucket[len('gs://'):]
136 file_system = self._gcs_file_system_provider.Create(bucket)
137 if 'dir' in gcs_config:
138 file_system = ChrootFileSystem(file_system, gcs_config['dir'])
140 elif 'github' in config:
141 github_config = config['github']
142 if 'owner' not in github_config or 'repo' not in github_config:
143 logging.error('%s: "github" must provide an "owner" and "repo"' % name)
144 return None
145 file_system = self._github_file_system_provider.Create(
146 github_config['owner'], github_config['repo'])
147 if 'dir' in github_config:
148 file_system = ChrootFileSystem(file_system, github_config['dir'])
150 else:
151 logging.error('%s: content provider type not supported' % name)
152 return None
154 return ContentProvider(name,
155 self._compiled_fs_factory,
156 file_system,
157 self._object_store_creator,
158 default_extensions=default_extensions,
159 supports_templates=supports_templates,
160 supports_zip=supports_zip)
162 def Cron(self):
163 def safe(name, action, callback):
164 '''Safely runs |callback| for a ContentProvider called |name| by
165 swallowing exceptions and turning them into a None return value. It's
166 important to run all ContentProvider Crons even if some of them fail.
168 try:
169 return callback()
170 except:
171 if not _IGNORE_MISSING_CONTENT_PROVIDERS[0]:
172 logging.error('Error %s Cron for ContentProvider "%s":\n%s' %
173 (action, name, traceback.format_exc()))
174 return None
176 futures = [(name, safe(name,
177 'initializing',
178 self._CreateContentProvider(name, config).Cron))
179 for name, config in self._GetConfig().iteritems()]
180 return Future(callback=
181 lambda: [safe(name, 'resolving', f.Get) for name, f in futures if f])