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/client/__init__.py +37 -0
- crate/client/_pep440.py +1 -0
- crate/client/blob.py +105 -0
- crate/client/connection.py +221 -0
- crate/client/converter.py +143 -0
- crate/client/cursor.py +321 -0
- crate/client/exceptions.py +101 -0
- crate/client/http.py +694 -0
- crate/testing/__init__.py +0 -0
- crate/testing/layer.py +428 -0
- crate/testing/util.py +95 -0
- crate-2.0.0.dev0.dist-info/LICENSE +178 -0
- crate-2.0.0.dev0.dist-info/METADATA +168 -0
- crate-2.0.0.dev0.dist-info/NOTICE +24 -0
- crate-2.0.0.dev0.dist-info/RECORD +17 -0
- crate-2.0.0.dev0.dist-info/WHEEL +5 -0
- crate-2.0.0.dev0.dist-info/top_level.txt +1 -0
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
|