crate 2.0.0.dev0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
crate/testing/layer.py ADDED
@@ -0,0 +1,428 @@
1
+ # -*- coding: utf-8; -*-
2
+ #
3
+ # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
4
+ # license agreements. See the NOTICE file distributed with this work for
5
+ # additional information regarding copyright ownership. Crate licenses
6
+ # this file to you under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License. You may
8
+ # obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
+ # License for the specific language governing permissions and limitations
16
+ # under the License.
17
+ #
18
+ # However, if you have executed another commercial license agreement
19
+ # with Crate these terms will supersede the license and you may use the
20
+ # software solely pursuant to the terms of the relevant commercial agreement.
21
+
22
+ # ruff: noqa: S603 # `subprocess` call: check for execution of untrusted input
23
+ # ruff: noqa: S202 # Uses of `tarfile.extractall()`
24
+
25
+ import io
26
+ import json
27
+ import logging
28
+ import os
29
+ import re
30
+ import shutil
31
+ import subprocess
32
+ import sys
33
+ import tarfile
34
+ import tempfile
35
+ import threading
36
+ import time
37
+
38
+ import urllib3
39
+
40
+ try:
41
+ from urllib.request import urlopen
42
+ except ImportError:
43
+ from urllib import urlopen # type: ignore[attr-defined,no-redef]
44
+
45
+
46
+ log = logging.getLogger(__name__)
47
+
48
+
49
+ CRATE_CONFIG_ERROR = (
50
+ 'crate_config must point to a folder or to a file named "crate.yml"'
51
+ )
52
+ HTTP_ADDRESS_RE = re.compile(
53
+ r".*\[(http|.*HttpServer.*)\s*] \[.*\] .*"
54
+ "publish_address {"
55
+ r"(?:inet\[[\w\d\.-]*/|\[)?"
56
+ r"(?:[\w\d\.-]+/)?"
57
+ r"(?P<addr>[\d\.:]+)"
58
+ r"(?:\])?"
59
+ "}"
60
+ )
61
+
62
+
63
+ def http_url_from_host_port(host, port):
64
+ if host and port:
65
+ if not isinstance(port, int):
66
+ try:
67
+ port = int(port)
68
+ except ValueError:
69
+ return None
70
+ return "{}:{}".format(prepend_http(host), port)
71
+ return None
72
+
73
+
74
+ def prepend_http(host):
75
+ if not re.match(r"^https?\:\/\/.*", host):
76
+ return "http://{}".format(host)
77
+ return host
78
+
79
+
80
+ def _download_and_extract(uri, directory):
81
+ sys.stderr.write(
82
+ "\nINFO: Downloading CrateDB archive from {} into {}".format(
83
+ uri, directory
84
+ )
85
+ )
86
+ sys.stderr.flush()
87
+ with io.BytesIO(urlopen(uri).read()) as tmpfile:
88
+ with tarfile.open(fileobj=tmpfile) as t:
89
+ t.extractall(directory)
90
+
91
+
92
+ def wait_for_http_url(log, timeout=30, verbose=False):
93
+ start = time.monotonic()
94
+ while True:
95
+ line = log.readline().decode("utf-8").strip()
96
+ elapsed = time.monotonic() - start
97
+ if verbose:
98
+ sys.stderr.write("[{:>4.1f}s]{}\n".format(elapsed, line))
99
+ m = HTTP_ADDRESS_RE.match(line)
100
+ if m:
101
+ return prepend_http(m.group("addr"))
102
+ elif elapsed > timeout:
103
+ return None
104
+
105
+
106
+ class OutputMonitor:
107
+ def __init__(self):
108
+ self.consumers = []
109
+
110
+ def consume(self, iterable):
111
+ for line in iterable:
112
+ for consumer in self.consumers:
113
+ consumer.send(line)
114
+
115
+ def start(self, proc):
116
+ self._stop_out_thread = threading.Event()
117
+ self._out_thread = threading.Thread(
118
+ target=self.consume, args=(proc.stdout,)
119
+ )
120
+ self._out_thread.daemon = True
121
+ self._out_thread.start()
122
+
123
+ def stop(self):
124
+ if self._out_thread is not None:
125
+ self._stop_out_thread.set()
126
+ self._out_thread.join()
127
+
128
+
129
+ class LineBuffer:
130
+ def __init__(self):
131
+ self.lines = []
132
+
133
+ def send(self, line):
134
+ self.lines.append(line.strip())
135
+
136
+
137
+ class CrateLayer:
138
+ """
139
+ This layer starts a Crate server.
140
+ """
141
+
142
+ __bases__ = ()
143
+
144
+ tmpdir = tempfile.gettempdir()
145
+ wait_interval = 0.2
146
+
147
+ @staticmethod
148
+ def from_uri(
149
+ uri,
150
+ name,
151
+ http_port="4200-4299",
152
+ transport_port="4300-4399",
153
+ settings=None,
154
+ directory=None,
155
+ cleanup=True,
156
+ verbose=False,
157
+ ):
158
+ """Download the Crate tarball from a URI and create a CrateLayer
159
+
160
+ :param uri: The uri that points to the Crate tarball
161
+ :param name: layer and cluster name
162
+ :param http_port: The http port on which Crate will listen
163
+ :param transport_port: the transport port on which Crate will listen
164
+ :param settings: A dictionary that contains Crate settings
165
+ :param directory: Where the tarball will be extracted to.
166
+ If this is None a temporary directory will be created.
167
+ :param clean: a boolean indicating if the directory should be removed
168
+ on teardown.
169
+ :param verbose: Set the log verbosity of the test layer
170
+ """
171
+ directory = directory or tempfile.mkdtemp()
172
+ filename = os.path.basename(uri)
173
+ crate_dir = re.sub(r"\.tar(\.gz)?$", "", filename)
174
+ crate_home = os.path.join(directory, crate_dir)
175
+
176
+ if os.path.exists(crate_home):
177
+ sys.stderr.write(
178
+ "\nWARNING: Not extracting CrateDB tarball"
179
+ " because folder already exists"
180
+ )
181
+ sys.stderr.flush()
182
+ else:
183
+ _download_and_extract(uri, directory)
184
+
185
+ layer = CrateLayer(
186
+ name=name,
187
+ crate_home=crate_home,
188
+ port=http_port,
189
+ transport_port=transport_port,
190
+ settings=settings,
191
+ verbose=verbose,
192
+ )
193
+ if cleanup:
194
+ tearDown = layer.tearDown
195
+
196
+ def new_teardown(*args, **kws):
197
+ shutil.rmtree(directory)
198
+ tearDown(*args, **kws)
199
+
200
+ layer.tearDown = new_teardown # type: ignore[method-assign]
201
+ return layer
202
+
203
+ def __init__(
204
+ self,
205
+ name,
206
+ crate_home,
207
+ crate_config=None,
208
+ port=None,
209
+ keepRunning=False,
210
+ transport_port=None,
211
+ crate_exec=None,
212
+ cluster_name=None,
213
+ host="127.0.0.1",
214
+ settings=None,
215
+ verbose=False,
216
+ env=None,
217
+ ):
218
+ """
219
+ :param name: layer name, is also used as the cluser name
220
+ :param crate_home: path to home directory of the crate installation
221
+ :param port: port on which crate should run
222
+ :param transport_port: port on which transport layer for crate should
223
+ run
224
+ :param crate_exec: alternative executable command
225
+ :param crate_config: alternative crate config file location.
226
+ Must be a directory or a file named 'crate.yml'
227
+ :param cluster_name: the name of the cluster to join/build. Will be
228
+ generated automatically if omitted.
229
+ :param host: the host to bind to. defaults to 'localhost'
230
+ :param settings: further settings that do not deserve a keyword
231
+ argument will be prefixed with ``es.``.
232
+ :param verbose: Set the log verbosity of the test layer
233
+ :param env: Set environment variables.
234
+ """
235
+ self.__name__ = name
236
+ if settings and isinstance(settings, dict):
237
+ # extra settings may override host/port specification!
238
+ self.http_url = http_url_from_host_port(
239
+ settings.get("network.host", host),
240
+ settings.get("http.port", port),
241
+ )
242
+ else:
243
+ self.http_url = http_url_from_host_port(host, port)
244
+
245
+ self.process = None
246
+ self.verbose = verbose
247
+ self.env = env or {}
248
+ self.env.setdefault("CRATE_USE_IPV4", "true")
249
+ self.env.setdefault("JAVA_HOME", os.environ.get("JAVA_HOME", ""))
250
+ self._stdout_consumers = []
251
+ self.conn_pool = urllib3.PoolManager(num_pools=1)
252
+
253
+ crate_home = os.path.abspath(crate_home)
254
+ if crate_exec is None:
255
+ start_script = "crate.bat" if sys.platform == "win32" else "crate"
256
+ crate_exec = os.path.join(crate_home, "bin", start_script)
257
+ if crate_config is None:
258
+ crate_config = os.path.join(crate_home, "config", "crate.yml")
259
+ elif (
260
+ os.path.isfile(crate_config)
261
+ and os.path.basename(crate_config) != "crate.yml"
262
+ ):
263
+ raise ValueError(CRATE_CONFIG_ERROR)
264
+ if cluster_name is None:
265
+ cluster_name = "Testing{0}".format(port or "Dynamic")
266
+ settings = self.create_settings(
267
+ crate_config,
268
+ cluster_name,
269
+ name,
270
+ host,
271
+ port or "4200-4299",
272
+ transport_port or "4300-4399",
273
+ settings,
274
+ )
275
+ # ES 5 cannot parse 'True'/'False' as booleans so convert to lowercase
276
+ start_cmd = (crate_exec,) + tuple(
277
+ [
278
+ "-C%s=%s"
279
+ % (
280
+ (key, str(value).lower())
281
+ if isinstance(value, bool)
282
+ else (key, value)
283
+ )
284
+ for key, value in settings.items()
285
+ ]
286
+ )
287
+
288
+ self._wd = wd = os.path.join(CrateLayer.tmpdir, "crate_layer", name)
289
+ self.start_cmd = start_cmd + ("-Cpath.data=%s" % wd,)
290
+
291
+ def create_settings(
292
+ self,
293
+ crate_config,
294
+ cluster_name,
295
+ node_name,
296
+ host,
297
+ http_port,
298
+ transport_port,
299
+ further_settings=None,
300
+ ):
301
+ settings = {
302
+ "discovery.type": "zen",
303
+ "discovery.initial_state_timeout": 0,
304
+ "node.name": node_name,
305
+ "cluster.name": cluster_name,
306
+ "network.host": host,
307
+ "http.port": http_port,
308
+ "path.conf": os.path.dirname(crate_config),
309
+ "transport.tcp.port": transport_port,
310
+ }
311
+ if further_settings:
312
+ settings.update(further_settings)
313
+ return settings
314
+
315
+ def wdPath(self):
316
+ return self._wd
317
+
318
+ @property
319
+ def crate_servers(self):
320
+ if self.http_url:
321
+ return [self.http_url]
322
+ return []
323
+
324
+ def setUp(self):
325
+ self.start()
326
+
327
+ def _clean(self):
328
+ if os.path.exists(self._wd):
329
+ shutil.rmtree(self._wd)
330
+
331
+ def start(self):
332
+ self._clean()
333
+ self.process = subprocess.Popen(
334
+ self.start_cmd, env=self.env, stdout=subprocess.PIPE
335
+ )
336
+ returncode = self.process.poll()
337
+ if returncode is not None:
338
+ raise SystemError(
339
+ "Failed to start server rc={0} cmd={1}".format(
340
+ returncode, self.start_cmd
341
+ )
342
+ )
343
+
344
+ if not self.http_url:
345
+ # try to read http_url from startup logs
346
+ # this is necessary if no static port is assigned
347
+ self.http_url = wait_for_http_url(
348
+ self.process.stdout, verbose=self.verbose
349
+ )
350
+
351
+ self.monitor = OutputMonitor()
352
+ self.monitor.start(self.process)
353
+
354
+ if not self.http_url:
355
+ self.stop()
356
+ else:
357
+ sys.stderr.write("HTTP: {}\n".format(self.http_url))
358
+ self._wait_for_start()
359
+ self._wait_for_master()
360
+ sys.stderr.write("\nCrate instance ready.\n")
361
+
362
+ def stop(self):
363
+ self.conn_pool.clear()
364
+ if self.process:
365
+ self.process.terminate()
366
+ self.process.communicate(timeout=10)
367
+ self.process.stdout.close()
368
+ self.process = None
369
+ self.monitor.stop()
370
+ self._clean()
371
+
372
+ def tearDown(self):
373
+ self.stop()
374
+
375
+ def _wait_for(self, validator):
376
+ start = time.monotonic()
377
+
378
+ line_buf = LineBuffer()
379
+ self.monitor.consumers.append(line_buf)
380
+
381
+ while True:
382
+ wait_time = time.monotonic() - start
383
+ try:
384
+ if validator():
385
+ break
386
+ except Exception as e:
387
+ self.stop()
388
+ raise e
389
+
390
+ if wait_time > 30:
391
+ for line in line_buf.lines:
392
+ log.error(line)
393
+ self.stop()
394
+ raise SystemError("Failed to start Crate instance in time.")
395
+ sys.stderr.write(".")
396
+ time.sleep(self.wait_interval)
397
+
398
+ self.monitor.consumers.remove(line_buf)
399
+
400
+ def _wait_for_start(self):
401
+ """Wait for instance to be started"""
402
+
403
+ # since crate 0.10.0 http may be ready before the cluster
404
+ # is fully available block here so that early requests
405
+ # after the layer starts don't result in 503
406
+ def validator():
407
+ try:
408
+ resp = self.conn_pool.request("HEAD", self.http_url)
409
+ return resp.status == 200
410
+ except Exception:
411
+ return False
412
+
413
+ self._wait_for(validator)
414
+
415
+ def _wait_for_master(self):
416
+ """Wait for master node"""
417
+
418
+ def validator():
419
+ resp = self.conn_pool.urlopen(
420
+ "POST",
421
+ "{server}/_sql".format(server=self.http_url),
422
+ headers={"Content-Type": "application/json"},
423
+ body='{"stmt": "select master_node from sys.cluster"}',
424
+ )
425
+ data = json.loads(resp.data.decode("utf-8"))
426
+ return resp.status == 200 and data["rows"][0][0]
427
+
428
+ self._wait_for(validator)
crate/testing/util.py ADDED
@@ -0,0 +1,95 @@
1
+ # -*- coding: utf-8; -*-
2
+ #
3
+ # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
4
+ # license agreements. See the NOTICE file distributed with this work for
5
+ # additional information regarding copyright ownership. Crate licenses
6
+ # this file to you under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License. You may
8
+ # obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
+ # License for the specific language governing permissions and limitations
16
+ # under the License.
17
+ #
18
+ # However, if you have executed another commercial license agreement
19
+ # with Crate these terms will supersede the license and you may use the
20
+ # software solely pursuant to the terms of the relevant commercial agreement.
21
+ import unittest
22
+
23
+
24
+ class ClientMocked:
25
+ active_servers = ["http://localhost:4200"]
26
+
27
+ def __init__(self):
28
+ self.response = {}
29
+ self._server_infos = ("http://localhost:4200", "my server", "2.0.0")
30
+
31
+ def sql(self, stmt=None, parameters=None, bulk_parameters=None):
32
+ return self.response
33
+
34
+ def server_infos(self, server):
35
+ return self._server_infos
36
+
37
+ def set_next_response(self, response):
38
+ self.response = response
39
+
40
+ def set_next_server_infos(self, server, server_name, version):
41
+ self._server_infos = (server, server_name, version)
42
+
43
+ def close(self):
44
+ pass
45
+
46
+
47
+ class ParametrizedTestCase(unittest.TestCase):
48
+ """
49
+ TestCase classes that want to be parametrized should
50
+ inherit from this class.
51
+
52
+ https://eli.thegreenplace.net/2011/08/02/python-unit-testing-parametrized-test-cases
53
+ """
54
+
55
+ def __init__(self, methodName="runTest", param=None):
56
+ super(ParametrizedTestCase, self).__init__(methodName)
57
+ self.param = param
58
+
59
+ @staticmethod
60
+ def parametrize(testcase_klass, param=None):
61
+ """Create a suite containing all tests taken from the given
62
+ subclass, passing them the parameter 'param'.
63
+ """
64
+ testloader = unittest.TestLoader()
65
+ testnames = testloader.getTestCaseNames(testcase_klass)
66
+ suite = unittest.TestSuite()
67
+ for name in testnames:
68
+ suite.addTest(testcase_klass(name, param=param))
69
+ return suite
70
+
71
+
72
+ class ExtraAssertions(unittest.TestCase):
73
+ """
74
+ Additional assert methods for unittest.
75
+
76
+ - https://github.com/python/cpython/issues/71339
77
+ - https://bugs.python.org/issue14819
78
+ - https://bugs.python.org/file43047/extra_assertions.patch
79
+ """
80
+
81
+ def assertIsSubclass(self, cls, superclass, msg=None):
82
+ try:
83
+ r = issubclass(cls, superclass)
84
+ except TypeError:
85
+ if not isinstance(cls, type):
86
+ self.fail(
87
+ self._formatMessage(msg, "%r is not a class" % (cls,))
88
+ )
89
+ raise
90
+ if not r:
91
+ self.fail(
92
+ self._formatMessage(
93
+ msg, "%r is not a subclass of %r" % (cls, superclass)
94
+ )
95
+ )
@@ -0,0 +1,178 @@
1
+
2
+
3
+ Apache License
4
+ Version 2.0, January 2004
5
+ http://www.apache.org/licenses/
6
+
7
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8
+
9
+ 1. Definitions.
10
+
11
+ "License" shall mean the terms and conditions for use, reproduction,
12
+ and distribution as defined by Sections 1 through 9 of this document.
13
+
14
+ "Licensor" shall mean the copyright owner or entity authorized by
15
+ the copyright owner that is granting the License.
16
+
17
+ "Legal Entity" shall mean the union of the acting entity and all
18
+ other entities that control, are controlled by, or are under common
19
+ control with that entity. For the purposes of this definition,
20
+ "control" means (i) the power, direct or indirect, to cause the
21
+ direction or management of such entity, whether by contract or
22
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
23
+ outstanding shares, or (iii) beneficial ownership of such entity.
24
+
25
+ "You" (or "Your") shall mean an individual or Legal Entity
26
+ exercising permissions granted by this License.
27
+
28
+ "Source" form shall mean the preferred form for making modifications,
29
+ including but not limited to software source code, documentation
30
+ source, and configuration files.
31
+
32
+ "Object" form shall mean any form resulting from mechanical
33
+ transformation or translation of a Source form, including but
34
+ not limited to compiled object code, generated documentation,
35
+ and conversions to other media types.
36
+
37
+ "Work" shall mean the work of authorship, whether in Source or
38
+ Object form, made available under the License, as indicated by a
39
+ copyright notice that is included in or attached to the work
40
+ (an example is provided in the Appendix below).
41
+
42
+ "Derivative Works" shall mean any work, whether in Source or Object
43
+ form, that is based on (or derived from) the Work and for which the
44
+ editorial revisions, annotations, elaborations, or other modifications
45
+ represent, as a whole, an original work of authorship. For the purposes
46
+ of this License, Derivative Works shall not include works that remain
47
+ separable from, or merely link (or bind by name) to the interfaces of,
48
+ the Work and Derivative Works thereof.
49
+
50
+ "Contribution" shall mean any work of authorship, including
51
+ the original version of the Work and any modifications or additions
52
+ to that Work or Derivative Works thereof, that is intentionally
53
+ submitted to Licensor for inclusion in the Work by the copyright owner
54
+ or by an individual or Legal Entity authorized to submit on behalf of
55
+ the copyright owner. For the purposes of this definition, "submitted"
56
+ means any form of electronic, verbal, or written communication sent
57
+ to the Licensor or its representatives, including but not limited to
58
+ communication on electronic mailing lists, source code control systems,
59
+ and issue tracking systems that are managed by, or on behalf of, the
60
+ Licensor for the purpose of discussing and improving the Work, but
61
+ excluding communication that is conspicuously marked or otherwise
62
+ designated in writing by the copyright owner as "Not a Contribution."
63
+
64
+ "Contributor" shall mean Licensor and any individual or Legal Entity
65
+ on behalf of whom a Contribution has been received by Licensor and
66
+ subsequently incorporated within the Work.
67
+
68
+ 2. Grant of Copyright License. Subject to the terms and conditions of
69
+ this License, each Contributor hereby grants to You a perpetual,
70
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
71
+ copyright license to reproduce, prepare Derivative Works of,
72
+ publicly display, publicly perform, sublicense, and distribute the
73
+ Work and such Derivative Works in Source or Object form.
74
+
75
+ 3. Grant of Patent License. Subject to the terms and conditions of
76
+ this License, each Contributor hereby grants to You a perpetual,
77
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
78
+ (except as stated in this section) patent license to make, have made,
79
+ use, offer to sell, sell, import, and otherwise transfer the Work,
80
+ where such license applies only to those patent claims licensable
81
+ by such Contributor that are necessarily infringed by their
82
+ Contribution(s) alone or by combination of their Contribution(s)
83
+ with the Work to which such Contribution(s) was submitted. If You
84
+ institute patent litigation against any entity (including a
85
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
86
+ or a Contribution incorporated within the Work constitutes direct
87
+ or contributory patent infringement, then any patent licenses
88
+ granted to You under this License for that Work shall terminate
89
+ as of the date such litigation is filed.
90
+
91
+ 4. Redistribution. You may reproduce and distribute copies of the
92
+ Work or Derivative Works thereof in any medium, with or without
93
+ modifications, and in Source or Object form, provided that You
94
+ meet the following conditions:
95
+
96
+ (a) You must give any other recipients of the Work or
97
+ Derivative Works a copy of this License; and
98
+
99
+ (b) You must cause any modified files to carry prominent notices
100
+ stating that You changed the files; and
101
+
102
+ (c) You must retain, in the Source form of any Derivative Works
103
+ that You distribute, all copyright, patent, trademark, and
104
+ attribution notices from the Source form of the Work,
105
+ excluding those notices that do not pertain to any part of
106
+ the Derivative Works; and
107
+
108
+ (d) If the Work includes a "NOTICE" text file as part of its
109
+ distribution, then any Derivative Works that You distribute must
110
+ include a readable copy of the attribution notices contained
111
+ within such NOTICE file, excluding those notices that do not
112
+ pertain to any part of the Derivative Works, in at least one
113
+ of the following places: within a NOTICE text file distributed
114
+ as part of the Derivative Works; within the Source form or
115
+ documentation, if provided along with the Derivative Works; or,
116
+ within a display generated by the Derivative Works, if and
117
+ wherever such third-party notices normally appear. The contents
118
+ of the NOTICE file are for informational purposes only and
119
+ do not modify the License. You may add Your own attribution
120
+ notices within Derivative Works that You distribute, alongside
121
+ or as an addendum to the NOTICE text from the Work, provided
122
+ that such additional attribution notices cannot be construed
123
+ as modifying the License.
124
+
125
+ You may add Your own copyright statement to Your modifications and
126
+ may provide additional or different license terms and conditions
127
+ for use, reproduction, or distribution of Your modifications, or
128
+ for any such Derivative Works as a whole, provided Your use,
129
+ reproduction, and distribution of the Work otherwise complies with
130
+ the conditions stated in this License.
131
+
132
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
133
+ any Contribution intentionally submitted for inclusion in the Work
134
+ by You to the Licensor shall be under the terms and conditions of
135
+ this License, without any additional terms or conditions.
136
+ Notwithstanding the above, nothing herein shall supersede or modify
137
+ the terms of any separate license agreement you may have executed
138
+ with Licensor regarding such Contributions.
139
+
140
+ 6. Trademarks. This License does not grant permission to use the trade
141
+ names, trademarks, service marks, or product names of the Licensor,
142
+ except as required for reasonable and customary use in describing the
143
+ origin of the Work and reproducing the content of the NOTICE file.
144
+
145
+ 7. Disclaimer of Warranty. Unless required by applicable law or
146
+ agreed to in writing, Licensor provides the Work (and each
147
+ Contributor provides its Contributions) on an "AS IS" BASIS,
148
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
149
+ implied, including, without limitation, any warranties or conditions
150
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
151
+ PARTICULAR PURPOSE. You are solely responsible for determining the
152
+ appropriateness of using or redistributing the Work and assume any
153
+ risks associated with Your exercise of permissions under this License.
154
+
155
+ 8. Limitation of Liability. In no event and under no legal theory,
156
+ whether in tort (including negligence), contract, or otherwise,
157
+ unless required by applicable law (such as deliberate and grossly
158
+ negligent acts) or agreed to in writing, shall any Contributor be
159
+ liable to You for damages, including any direct, indirect, special,
160
+ incidental, or consequential damages of any character arising as a
161
+ result of this License or out of the use or inability to use the
162
+ Work (including but not limited to damages for loss of goodwill,
163
+ work stoppage, computer failure or malfunction, or any and all
164
+ other commercial damages or losses), even if such Contributor
165
+ has been advised of the possibility of such damages.
166
+
167
+ 9. Accepting Warranty or Additional Liability. While redistributing
168
+ the Work or Derivative Works thereof, You may choose to offer,
169
+ and charge a fee for, acceptance of support, warranty, indemnity,
170
+ or other liability obligations and/or rights consistent with this
171
+ License. However, in accepting such obligations, You may act only
172
+ on Your own behalf and on Your sole responsibility, not on behalf
173
+ of any other Contributor, and only if You agree to indemnify,
174
+ defend, and hold each Contributor harmless for any liability
175
+ incurred by, or claims asserted against, such Contributor by reason
176
+ of your accepting any such warranty or additional liability.
177
+
178
+ END OF TERMS AND CONDITIONS