pyrelukko 0.2.0__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pyrelukko might be problematic. Click here for more details.

Files changed (40) hide show
  1. pyrelukko-0.4.0/.gitlab-ci.yml +173 -0
  2. {pyrelukko-0.2.0 → pyrelukko-0.4.0}/PKG-INFO +7 -2
  3. pyrelukko-0.4.0/cicd/build-py-rf-image.sh +48 -0
  4. pyrelukko-0.4.0/cicd/run_docs_pages.sh +17 -0
  5. pyrelukko-0.4.0/cicd/run_pylint.sh +24 -0
  6. pyrelukko-0.4.0/cicd/run_pytest.sh +15 -0
  7. pyrelukko-0.4.0/cicd/run_shellcheck.sh +14 -0
  8. pyrelukko-0.4.0/container/Containerfile +5 -0
  9. pyrelukko-0.4.0/container/requirements.txt +5 -0
  10. pyrelukko-0.4.0/pyproject.toml +65 -0
  11. {pyrelukko-0.2.0 → pyrelukko-0.4.0}/src/pyrelukko/pyrelukko.py +38 -14
  12. pyrelukko-0.2.0/tests/conftest.py → pyrelukko-0.4.0/src/pyrelukko/testcontainers.py +26 -69
  13. {pyrelukko-0.2.0 → pyrelukko-0.4.0}/src/pyrelukko/version.py +1 -1
  14. pyrelukko-0.4.0/tests/cert/README.md +11 -0
  15. pyrelukko-0.4.0/tests/cert/relukko.crt +18 -0
  16. pyrelukko-0.4.0/tests/cert/relukko.csr +16 -0
  17. pyrelukko-0.4.0/tests/cert/relukko.key +28 -0
  18. pyrelukko-0.4.0/tests/cert/rootCA.crt +19 -0
  19. pyrelukko-0.4.0/tests/cert/rootCA.der +0 -0
  20. pyrelukko-0.4.0/tests/cert/rootCA.key +28 -0
  21. pyrelukko-0.4.0/tests/cert/rootCA.srl +1 -0
  22. pyrelukko-0.4.0/tests/conftest.py +79 -0
  23. {pyrelukko-0.2.0 → pyrelukko-0.4.0}/tests/test_relukko.py +61 -7
  24. pyrelukko-0.2.0/.pylintrc.toml +0 -9
  25. pyrelukko-0.2.0/pyproject.toml +0 -39
  26. pyrelukko-0.2.0/tests/cert/README.md +0 -8
  27. pyrelukko-0.2.0/tests/cert/relukko.crt +0 -21
  28. pyrelukko-0.2.0/tests/cert/relukko.csr +0 -16
  29. pyrelukko-0.2.0/tests/cert/relukko.der +0 -0
  30. pyrelukko-0.2.0/tests/cert/relukko.key +0 -28
  31. pyrelukko-0.2.0/tests/cert/rootCA.crt +0 -21
  32. pyrelukko-0.2.0/tests/cert/rootCA.der +0 -0
  33. pyrelukko-0.2.0/tests/cert/rootCA.key +0 -28
  34. pyrelukko-0.2.0/tests/cert/rootCA.srl +0 -1
  35. pyrelukko-0.2.0/tests/initdb.d/20240930160154_init.up.sql +0 -19
  36. {pyrelukko-0.2.0 → pyrelukko-0.4.0}/LICENSE +0 -0
  37. {pyrelukko-0.2.0 → pyrelukko-0.4.0}/README.md +0 -0
  38. {pyrelukko-0.2.0 → pyrelukko-0.4.0}/src/pyrelukko/__init__.py +0 -0
  39. {pyrelukko-0.2.0 → pyrelukko-0.4.0}/src/pyrelukko/retry.py +0 -0
  40. /pyrelukko-0.2.0/tests/cert/47615cfb.0 → /pyrelukko-0.4.0/tests/cert/5d868fca.0 +0 -0
@@ -0,0 +1,173 @@
1
+ variables:
2
+ FF_TIMESTAMPS: true
3
+ BUILD_IMAGE:
4
+ description: Used in rules, if "true" and web triggered it builds the image!
5
+ value: "false"
6
+ options:
7
+ - "false"
8
+ - "true"
9
+ IMAGE_NAME: py-rf-image
10
+ IMAGE_TAG:
11
+ description: >-
12
+ The "tag" added to the image name for the test / lint image.
13
+ Remove this when you trigger by hand an image build and want it to use
14
+ the new built image with Git short sha tag (X icon on the right)!
15
+ value: "latest"
16
+ RUN_PYTEST:
17
+ description: Used in rules, if "true" and web triggered run the pytests.
18
+ value: "false"
19
+ options:
20
+ - "false"
21
+ - "true"
22
+ RELUKKO_HOSTNAME: relukko
23
+ RELUKKO_BIND_PORT: 3000
24
+ RELUKKO_API_KEY: "somerandomkey"
25
+ POSTGRES_USER: relukko
26
+ POSTGRES_PASSWORD: okkuler
27
+ POSTGRES_DB: relukko
28
+ POSTGRES_HOSTNAME: postgres
29
+ # RUN_DOCS_PUBLISH:
30
+ # description: >-
31
+ # Used in rules, if "true" and web triggered it generates the docs and
32
+ # publishes them!
33
+ # value: "false"
34
+ # options:
35
+ # - "false"
36
+ # - "true"
37
+
38
+
39
+ build-py-rf-image:
40
+ stage: build
41
+ image: docker:25
42
+ services:
43
+ - name: docker:25-dind
44
+ alias: docker
45
+ before_script:
46
+ - docker info
47
+ before_script:
48
+ - echo "Login to Gitlab container registry"
49
+ - echo "$CI_REGISTRY_PASSWORD" | docker login --username $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
50
+ script:
51
+ - echo "Build container image..."
52
+ - ./cicd/build-py-rf-image.sh
53
+ - echo "Export IMAGE_TAG var to next stage!"
54
+ - echo "IMAGE_TAG=${CI_COMMIT_SHORT_SHA}" > image_tag.env
55
+ - echo "Build complete."
56
+ artifacts:
57
+ reports:
58
+ dotenv: image_tag.env
59
+ rules:
60
+ - if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
61
+ changes:
62
+ - .gitlab-ci.yml
63
+ - cicd/build-py-rf-image.sh
64
+ - container/**/*
65
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
66
+ changes:
67
+ - .gitlab-ci.yml
68
+ - cicd/build-py-rf-image.sh
69
+ - container/**/*
70
+ - if: $CI_PIPELINE_SOURCE == "web" && $BUILD_IMAGE == "true"
71
+ when: always
72
+
73
+
74
+ run-pylint:
75
+ stage: test
76
+ image: "${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${IMAGE_TAG}"
77
+ rules:
78
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
79
+ changes:
80
+ - src/**/*.py
81
+ - if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
82
+ changes:
83
+ - src/**/*.py
84
+ before_script:
85
+ - echo "Running PyLint."
86
+ script:
87
+ - ./cicd/run_pylint.sh
88
+ artifacts:
89
+ when: always
90
+ expire_in: 2 week
91
+ paths:
92
+ - pylint.out.*
93
+
94
+
95
+ run-pytest:
96
+ stage: test
97
+ services:
98
+ - name: postgres:16
99
+ alias: postgres
100
+ - name: registry.gitlab.com/relukko/relukko:latest
101
+ alias: relukko
102
+ variables:
103
+ DATABASE_URL: "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}"
104
+ RELUKKO_USER: "relukko"
105
+ RELUKKO_PASSWORD: "relukko"
106
+ RELUKKO_BIND_ADDR: "0.0.0.0"
107
+ HEALTHCHECK_TCP_PORT: "${RELUKKO_BIND_PORT}"
108
+ variables:
109
+ FF_NETWORK_PER_BUILD: 1
110
+ CI_HAS_RELUKKO: true
111
+ CI_RELUKKO_BASE_URL: "http://${RELUKKO_HOSTNAME}:${RELUKKO_BIND_PORT}"
112
+ CI_RELUKKO_API_KEY: "${RELUKKO_API_KEY}"
113
+ parallel:
114
+ matrix:
115
+ - PIPELINE_PY_VERSION: ['3.11', '3.12', '3.13']
116
+ image: "registry.gitlab.com/relukko/py-tox-images/py-$PIPELINE_PY_VERSION-tox"
117
+ rules:
118
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
119
+ changes:
120
+ - src/**/*.py
121
+ - tests/test_*.py
122
+ - tests/conftest.py
123
+ - if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
124
+ changes:
125
+ - src/**/*.py
126
+ - tests/test_*.py
127
+ - tests/conftest.py
128
+ - if: $CI_PIPELINE_SOURCE == "web" && $RUN_PYTEST == "true"
129
+ when: always
130
+ before_script:
131
+ - echo "Running PyTest."
132
+ script:
133
+ - ./cicd/run_pytest.sh
134
+ artifacts:
135
+ when: always
136
+ expire_in: 2 week
137
+ paths:
138
+ - pytest-junit.xml
139
+ reports:
140
+ junit: pytest-junit.xml
141
+
142
+
143
+ run-shellcheck:
144
+ stage: test
145
+ image: "${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${IMAGE_TAG}"
146
+ rules:
147
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
148
+ changes:
149
+ - cicd/*.sh
150
+ - if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
151
+ changes:
152
+ - cicd/*.sh
153
+ before_script:
154
+ - echo "Running ShellCheck."
155
+ script:
156
+ - ./cicd/run_shellcheck.sh
157
+
158
+ # pages:
159
+ # stage: deploy
160
+ # image: "${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${IMAGE_TAG}"
161
+ # rules:
162
+ # - if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
163
+ # changes:
164
+ # - src/**/*.py
165
+ # - if: $CI_PIPELINE_SOURCE == "web" && $RUN_DOCS_PUBLISH == "true"
166
+ # when: always
167
+ # before_script:
168
+ # - echo "Running Docs generation and publishing..."
169
+ # script:
170
+ # - ./cicd/run_docs_pages.sh
171
+ # artifacts:
172
+ # paths:
173
+ # - public
@@ -1,16 +1,21 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pyrelukko
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Relukko client.
5
5
  Author-email: Reto Zingg <g.d0b3rm4n@gmail.com>
6
- Requires-Python: >=3.12
6
+ Requires-Python: >=3.10
7
7
  Description-Content-Type: text/markdown
8
8
  Classifier: Development Status :: 3 - Alpha
9
9
  Classifier: Intended Audience :: Developers
10
10
  Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Topic :: Internet :: WWW/HTTP
12
12
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
13
17
  Requires-Dist: requests >=2.32.3
18
+ Requires-Dist: websockets >= 13.1
14
19
  Project-URL: Homepage, https://gitlab.com/relukko/pyrelukko
15
20
  Project-URL: Issues, https://gitlab.com/relukko/pyrelukko/-/issues
16
21
 
@@ -0,0 +1,48 @@
1
+ #!/bin/sh
2
+
3
+ set -e
4
+
5
+ SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
6
+ REPO_DIR="${SCRIPT_DIR}/.."
7
+ CONTAINER_DIR="${REPO_DIR}/container"
8
+ CONTAINER_CMD="docker"
9
+
10
+ # IMAGE_NAME should normally come from .gitlab-ci.yml file,
11
+ # set default for local usage.
12
+ if [ -z "${IMAGE_NAME+x}" ]; then
13
+ IMAGE_NAME=py-rf-image-local
14
+ fi
15
+
16
+ cd "${CONTAINER_DIR}" || exit
17
+
18
+ ${CONTAINER_CMD} build -t "${IMAGE_NAME}:latest" --file Containerfile .
19
+
20
+ ${CONTAINER_CMD} images
21
+
22
+ if [ -n "${CI_REGISTRY_IMAGE}" ]; then
23
+ # In Gitlab
24
+
25
+ # Tag with Git commit short sha and push
26
+ IMAGE_WITH_GIT_SHA="${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}"
27
+ echo "IMAGE_WITH_GIT_SHA: ${IMAGE_WITH_GIT_SHA}"
28
+
29
+ ${CONTAINER_CMD} tag "${IMAGE_NAME}:latest" "${IMAGE_WITH_GIT_SHA}"
30
+ ${CONTAINER_CMD} push "${IMAGE_WITH_GIT_SHA}"
31
+
32
+ if [ "${CI_COMMIT_BRANCH}" = "${CI_DEFAULT_BRANCH}" ]; then
33
+ # We run in default branch, also tag with latest
34
+ # Tag with "latest" (overwrites last 'latest' in registry) and push
35
+ IMAGE_WITH_LATEST="${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:latest"
36
+
37
+ echo "Tag image with as 'latest': ${IMAGE_WITH_LATEST}"
38
+
39
+ ${CONTAINER_CMD} tag "${IMAGE_NAME}:latest" "${IMAGE_WITH_LATEST}"
40
+ ${CONTAINER_CMD} push "${IMAGE_WITH_LATEST}"
41
+ fi
42
+ else
43
+ echo "Runs locally, no pushing!"
44
+ fi
45
+
46
+ cd -
47
+
48
+ # vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
@@ -0,0 +1,17 @@
1
+ #!/bin/bash
2
+
3
+ SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
4
+ REPO_DIR="${SCRIPT_DIR}/.."
5
+ PUBLIC_DIR="${REPO_DIR}/public"
6
+
7
+ # Main script execution
8
+ main() {
9
+ mkdir -p "${PUBLIC_DIR}"
10
+ # libdoc --pythonpath ./src Relukko "${PUBLIC_DIR}/index.html"
11
+ echo "Not Implemented yet"
12
+ }
13
+
14
+ # Execute the main function
15
+ main
16
+
17
+ # vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
@@ -0,0 +1,24 @@
1
+ #!/bin/bash
2
+
3
+ SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
4
+ REPO_DIR="${SCRIPT_DIR}/.."
5
+
6
+ # Main script execution
7
+ main() {
8
+ pylint --verbose \
9
+ --rcfile "${REPO_DIR}/pyproject.toml" \
10
+ --output-format=json:pylint.out.json,parseable:pylint.out.txt,text \
11
+ "${REPO_DIR}"
12
+ pylint_exit_code=$?
13
+
14
+ pylint-json2html \
15
+ -o "${REPO_DIR}/pylint.out.html" \
16
+ "${REPO_DIR}/pylint.out.json"
17
+
18
+ return ${pylint_exit_code}
19
+ }
20
+
21
+ # Execute the main function
22
+ main
23
+
24
+ # vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
@@ -0,0 +1,15 @@
1
+ #!/bin/bash
2
+
3
+ # SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
4
+ # REPO_DIR="${SCRIPT_DIR}/.."
5
+
6
+ # Main script execution
7
+ main() {
8
+ # pytest --junit-xml "${REPO_DIR}/pytest-junit.xml"
9
+ tox run --skip-missing-interpreters
10
+ }
11
+
12
+ # Execute the main function
13
+ main
14
+
15
+ # vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
@@ -0,0 +1,14 @@
1
+ #!/bin/bash
2
+
3
+ SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
4
+ REPO_DIR="${SCRIPT_DIR}/.."
5
+
6
+ # Main script execution
7
+ main() {
8
+ shellcheck "${REPO_DIR}"/cicd/*.sh
9
+ }
10
+
11
+ # Execute the main function
12
+ main
13
+
14
+ # vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
@@ -0,0 +1,5 @@
1
+ FROM python:3.13-alpine
2
+
3
+ RUN apk add --no-cache bash shellcheck
4
+ COPY requirements.txt /opt/requirements.txt
5
+ RUN pip install --no-cache-dir -r /opt/requirements.txt
@@ -0,0 +1,5 @@
1
+ pylint==3.3.1
2
+ pylint-json2html==0.5.0
3
+ pytest==8.3.3
4
+ requests==2.32.3
5
+ websockets==13.1
@@ -0,0 +1,65 @@
1
+ [project]
2
+ name = "pyrelukko"
3
+ authors = [
4
+ {name = "Reto Zingg", email = "g.d0b3rm4n@gmail.com"},
5
+ ]
6
+ readme = "README.md"
7
+
8
+ # https://pypi.org/pypi?%3Aaction=list_classifiers
9
+ classifiers = [
10
+ "Development Status :: 3 - Alpha",
11
+ "Intended Audience :: Developers",
12
+ "License :: OSI Approved :: MIT License",
13
+ "Topic :: Internet :: WWW/HTTP",
14
+ "Topic :: Software Development :: Libraries :: Python Modules",
15
+ "Programming Language :: Python :: 3.11",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3.13",
18
+ "Programming Language :: Python :: 3.14",
19
+ ]
20
+ requires-python = ">=3.10"
21
+ dynamic = ["version", "description"]
22
+ dependencies = [
23
+ "requests >=2.32.3",
24
+ "websockets >= 13.1",
25
+ ]
26
+
27
+ [tool.flit.sdist]
28
+ exclude = [".gitignore", "demo.py"]
29
+
30
+ [project.urls]
31
+ Homepage = "https://gitlab.com/relukko/pyrelukko"
32
+ Issues = "https://gitlab.com/relukko/pyrelukko/-/issues"
33
+
34
+ [build-system]
35
+ requires = ["flit_core >=3.2,<4", "semantic-version >= 2.10"]
36
+ build-backend = "flit_core.buildapi"
37
+
38
+ [tool.pytest.ini_options]
39
+ testpaths = [
40
+ "tests",
41
+ ]
42
+ pythonpath = [
43
+ "src",
44
+ ]
45
+
46
+ [tool.tox]
47
+ requires = ["tox>=4.23"]
48
+ env_list = ["3.11", "3.12", "3.13"]
49
+
50
+ [tool.tox.env_run_base]
51
+ description = "Run test under {base_python}"
52
+ commands = [["pytest", "--junit-xml", "pytest-junit.xml"]]
53
+ deps = ["httpx", "pytest", "testcontainers"]
54
+ # set_env = { VIRTUALENV_DISCOVERY = "pyenv" }
55
+ pass_env = [ "TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE", "DOCKER_HOST", "CI_HAS_RELUKKO", "CI_RELUKKO_*" ]
56
+
57
+ [tool.pylint.main]
58
+ # Files or directories to be skipped. They should be base names, not paths.
59
+ ignore = ["LICENSE", "pyproject.toml", "version.py",
60
+ "README.md", "demo.py", "conftest.py", ".tox"]
61
+
62
+ # Files or directories matching the regular expression patterns are skipped. The
63
+ # regex matches against base names, not paths. The default value ignores Emacs
64
+ # file locks
65
+ ignore-patterns = ["^\\.#", "^test_"]
@@ -33,9 +33,18 @@ RETRY_KWARGS = [
33
33
  'delay',
34
34
  'backoff',
35
35
  'max_delay',
36
- 'exceptions'
36
+ 'exceptions',
37
37
  ]
38
38
 
39
+ OWN_KWARGS = [
40
+ 'acquire_wait_for_timeout',
41
+ 'acquire_modulo',
42
+ 'ws_ping_interval',
43
+ 'ws_ping_timeout',
44
+ 'ws_wait_for_timeout',
45
+ ]
46
+ OWN_KWARGS.extend(RETRY_KWARGS)
47
+
39
48
  logger = logging.getLogger(__name__)
40
49
 
41
50
 
@@ -66,7 +75,12 @@ class RelukkoClient:
66
75
  )
67
76
  self._setup_session(api_key, **kwargs)
68
77
  self._setup_http_adapters_retry(**kwargs)
69
- self._setup_pyrelukko_retry(**kwargs)
78
+ self.ws_wait_for_timeout = 2
79
+ self.ws_ping_interval = 60
80
+ self.ws_ping_timeout = 20
81
+ self.acquire_wait_for_timeout = 2
82
+ self.acquire_modulo = 100
83
+ self._setup_pyrelukko_kwargs(**kwargs)
70
84
 
71
85
  self.base_url = self._setup_base_url(base_url)
72
86
  self.ws_url = self._setup_ws_url(str(self.base_url))
@@ -90,10 +104,10 @@ class RelukkoClient:
90
104
  self.base_url = self._setup_base_url(base_url or self.base_url)
91
105
  self.ws_url = self._setup_ws_url(str(self.base_url))
92
106
  self._setup_ssl_ctx(**kwargs)
93
- self._setup_pyrelukko_retry(**kwargs)
107
+ self._setup_pyrelukko_kwargs(**kwargs)
94
108
 
95
- def _setup_pyrelukko_retry(self, **kwargs):
96
- for kwarg in RETRY_KWARGS:
109
+ def _setup_pyrelukko_kwargs(self, **kwargs):
110
+ for kwarg in OWN_KWARGS:
97
111
  setattr(
98
112
  self,
99
113
  kwarg,
@@ -151,7 +165,7 @@ class RelukkoClient:
151
165
 
152
166
  def _setup_ws_url(self, ws_url: str) -> Url:
153
167
  url = ws_url.replace("http", "ws", 1)
154
- return parse_url(f"{url}/deletions")
168
+ return parse_url(f"{url}/ws/broadcast")
155
169
 
156
170
  def _setup_base_url(self, base_url: Union[Url, str]) -> Url:
157
171
  if isinstance(base_url, str):
@@ -174,14 +188,17 @@ class RelukkoClient:
174
188
  additional_headers=additional_headers,
175
189
  ssl=self.ssl_ctx,
176
190
  logger=logger,
191
+ ping_interval=self.ws_ping_interval,
192
+ ping_timeout=self.ws_ping_timeout,
177
193
  ) as websocket:
178
194
  while self.ws_running.is_set():
179
195
  try:
180
- ws_message = await asyncio.wait_for(websocket.recv(), timeout=2)
196
+ ws_message = await asyncio.wait_for(
197
+ websocket.recv(), timeout=self.ws_wait_for_timeout)
181
198
  if ws_message:
182
199
  logger.debug("Received message: '%s'", ws_message)
183
200
  msg: Dict = json.loads(ws_message)
184
- if msg.get('deletions'):
201
+ if msg.get('deleted'):
185
202
  # Signal the HTTP thread to wake up
186
203
  self.message_received.set()
187
204
  except TimeoutError:
@@ -199,19 +216,25 @@ class RelukkoClient:
199
216
  """
200
217
 
201
218
  start_time = time.time()
202
-
219
+ loop_counter = 0
220
+ got_message = False
221
+ res = None
203
222
  while True:
204
223
  elapsed_time = time.time() - start_time
205
224
  if elapsed_time > max_run_time:
206
225
  self.ws_running.clear()
207
226
  _thread_store.insert(0, None)
208
227
  return
209
-
210
- res = self._make_request(
211
- url=url, method="POST", payload=payload)
228
+ # If self.message_recieved is True try to get lock ASAP!
229
+ # Otherwise only in every Xth run in case websocket broke.
230
+ if got_message or loop_counter % self.acquire_modulo == 0:
231
+ res = self._make_request(
232
+ url=url, method="POST", payload=payload)
233
+ loop_counter += 1
212
234
  if res is None:
213
235
  # Conflict 409
214
- self.message_received.wait(timeout=30)
236
+ got_message = self.message_received.wait(
237
+ timeout=self.acquire_wait_for_timeout)
215
238
  self.message_received.clear()
216
239
  else:
217
240
  _thread_store.insert(0, res)
@@ -277,7 +300,8 @@ class RelukkoClient:
277
300
 
278
301
  thread_store = []
279
302
  http_thread = threading.Thread(
280
- target=self._acquire_relukko, args=(url, max_run_time, payload, thread_store))
303
+ target=self._acquire_relukko,
304
+ args=(url, max_run_time, payload, thread_store))
281
305
  http_thread.start()
282
306
  http_thread.join()
283
307
  self.ws_listener.join()
@@ -1,24 +1,30 @@
1
+ # pylint: skip-file
2
+ """
3
+ Testcontainers to be used in PyTests, for a fixture see tests/conftest.py
4
+ (relukko_backend):
5
+
6
+ @pytest.fixture(scope="session")
7
+ def relukko_backend():
8
+ with Network() as rl_net:
9
+ with RelukkoDbContainer(net=rl_net,
10
+ image="postgres:16", hostname="relukkodb") as _db:
11
+ db_url = "postgresql://relukko:relukko@relukkodb/relukko"
12
+ with RelukkoContainer(rl_net, db_url=db_url) as backend:
13
+ relukko = RelukkoClient(
14
+ base_url=backend.get_api_url(), api_key="somekey")
15
+ yield relukko, backend
16
+ """
1
17
  import socket
2
- import ssl
3
- import threading
4
- from pathlib import Path
5
- from typing import List
6
18
 
7
- import pytest
8
19
  from testcontainers.core.network import Network
9
20
  from testcontainers.core.waiting_utils import wait_container_is_ready
10
21
  from testcontainers.generic import ServerContainer
11
22
  from testcontainers.postgres import PostgresContainer
12
23
 
13
- from pyrelukko import RelukkoClient
14
-
15
- SCRIPT_DIR = Path(__file__).parent.absolute()
16
- INITDB_DIR = SCRIPT_DIR / "initdb.d"
17
-
18
24
 
19
25
  class RelukkoContainer(ServerContainer):
20
26
  def __init__(self, net: Network,
21
- image="registry.gitlab.com/relukko/relukko:0.9.0", db_url=None):
27
+ image="registry.gitlab.com/relukko/relukko:latest", db_url=None):
22
28
  self.db_url = db_url
23
29
  self.net = net
24
30
  super(RelukkoContainer, self).__init__(image=image, port=3000)
@@ -39,12 +45,19 @@ class RelukkoContainer(ServerContainer):
39
45
 
40
46
 
41
47
  class RelukkoDbContainer(PostgresContainer):
42
- def __init__(self, net: Network, image: str = "postgres:latest", port: int = 5432, username: str | None = None, password: str | None = None, dbname: str | None = None, driver: str | None = "psycopg2", **kwargs) -> None:
48
+ def __init__(
49
+ self, net: Network,
50
+ image: str = "postgres:latest",
51
+ port: int = 5432,
52
+ username: str | None = None,
53
+ password: str | None = None,
54
+ dbname: str | None = None,
55
+ driver: str | None = "psycopg2",
56
+ **kwargs) -> None:
43
57
  self.net = net
44
58
  super().__init__(image, port, username, password, dbname, driver, **kwargs)
45
59
 
46
60
  def _configure(self) -> None:
47
- self.with_volume_mapping(INITDB_DIR, "/docker-entrypoint-initdb.d", "Z")
48
61
  self.with_env("POSTGRES_USER", "relukko")
49
62
  self.with_env("POSTGRES_PASSWORD", "relukko")
50
63
  self.with_env("POSTGRES_DB", "relukko")
@@ -71,59 +84,3 @@ class RelukkoDbContainer(PostgresContainer):
71
84
  buf = sock.recv(40)
72
85
  if len(buf) == 0 and "SCRAM-SHA" not in buf:
73
86
  raise ConnectionError
74
-
75
-
76
- @pytest.fixture(scope="session")
77
- def relukko_backend():
78
- with Network() as rl_net:
79
- with RelukkoDbContainer(net=rl_net,
80
- image="postgres:16", hostname="relukkodb") as _db:
81
- db_url = "postgresql://relukko:relukko@relukkodb/relukko"
82
- with RelukkoContainer(rl_net, db_url=db_url) as backend:
83
- relukko = RelukkoClient(
84
- base_url=backend.get_api_url(), api_key="somekey")
85
- yield relukko, backend
86
-
87
- @pytest.fixture(scope="function")
88
- def tls_listener():
89
-
90
- certfile = SCRIPT_DIR / "cert" / "relukko.crt"
91
- keyfile = SCRIPT_DIR / "cert" / "relukko.key"
92
-
93
- def run_server(port_info: List, keep_running: threading.Event):
94
- # Create a TCP socket
95
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock:
96
- sock.bind(("127.0.0.1", 0))
97
- sock.listen(5) # Listen for incoming connections
98
-
99
- _, assigned_port = sock.getsockname()
100
- port_info.append(assigned_port)
101
-
102
- # Wrap the socket with TLS
103
- context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
104
- context.load_cert_chain(certfile=certfile, keyfile=keyfile)
105
- with context.wrap_socket(sock, server_side=True) as ssock:
106
-
107
- ssock.settimeout(1)
108
-
109
- while keep_running.is_set():
110
- try:
111
- client_socket, _ = ssock.accept()
112
- client_socket.sendall(b"Hello, TLS Client!")
113
- client_socket.close()
114
- except Exception as _:
115
- continue
116
-
117
- port_info = []
118
- keep_running = threading.Event()
119
- keep_running.set()
120
- thread = threading.Thread(target=run_server, args=(port_info, keep_running))
121
- thread.start()
122
-
123
- while not port_info:
124
- pass
125
-
126
- yield thread, port_info[0]
127
-
128
- keep_running.clear()
129
- thread.join()
@@ -1,2 +1,2 @@
1
1
  # pylint: disable=all
2
- __version__ = "0.2.0"
2
+ __version__ = "0.4.0"
@@ -0,0 +1,11 @@
1
+ # How To
2
+ ```
3
+ openssl req -subj '/CN=Relukko CA' -x509 -sha256 -days 7300 -noenc -newkey rsa:2048 -keyout rootCA.key -out rootCA.crt -addext keyUsage=critical,cRLSign,digitalSignature,keyCertSign
4
+
5
+ ln -s rootCA.crt "$(openssl x509 -hash -noout -in rootCA.crt).0"
6
+
7
+ openssl req -subj '/CN=relukko' -newkey rsa:2048 -noenc -keyout relukko.key -out relukko.csr -addext keyUsage=critical,digitalSignature,keyEncipherment,keyAgreement -addext extendedKeyUsage=critical,serverAuth
8
+
9
+ openssl x509 -req -CA rootCA.crt -CAkey rootCA.key -in relukko.csr -out relukko.crt -days 7299 -CAcreateserial
10
+ openssl x509 -inform PEM -in rootCA.crt -outform DER -out rootCA.der
11
+ ```
@@ -0,0 +1,18 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIC9zCCAd+gAwIBAgIUXFQN+AOO7KFdiVNVFman8BLkRfkwDQYJKoZIhvcNAQEL
3
+ BQAwFTETMBEGA1UEAwwKUmVsdWtrbyBDQTAeFw0yNDExMTYyMTM5MTNaFw00NDEx
4
+ MTAyMTM5MTNaMBIxEDAOBgNVBAMMB3JlbHVra28wggEiMA0GCSqGSIb3DQEBAQUA
5
+ A4IBDwAwggEKAoIBAQCljry3blOWFJRdinzcH4XzRnvKlhhofBhtRsPH+pvjmrHO
6
+ v52rXj1XeHqXaEBevhNiN7lP5eg2UgOrs0KAVXYR2Na9Mxrh8ACX+IqeEJF9BKgO
7
+ 4t2RvItDU3SWxV3dIYMasBhe/iDXDKKZd/jVOiluL9zuxo9ltDagZxc72jWMnvrO
8
+ lvlnZVEtH4n9i+hnx1daWRZG87DjO6d8I8F8nX6NTqJgBlrRm8jlcq2N/G8eHTFq
9
+ OrLY4xk4CF+aVExpJrEGprfhNkbE025tJIbGNM9Xv+W0sI/nhEwJdv4ifEP/b+44
10
+ n+KHrAurOboFYmRcoZ9Vn4C1KuYBvDOpgkwOs1wtAgMBAAGjQjBAMB0GA1UdDgQW
11
+ BBSy6DZz/N1PW1mkQY7HUj3Fz6yZOjAfBgNVHSMEGDAWgBT+sHazfmyczKb0BV6r
12
+ Z1JQ8Sfx9zANBgkqhkiG9w0BAQsFAAOCAQEAL1c1l3Azna1mh86haErurYk3EsiH
13
+ KDGprRubdB5qfpa2GdAkhitp9juKYdKGdw877eOl5z7AMeMWAA8WXQ3z7jkQkUhU
14
+ 9wsZxNyHQajq6Lo9KsRxZiRKZX/fxagcnDem+4xTvzSlJoAT5p/CO5E31odwWkZN
15
+ bg79I36nk87opISXzNAbMBcbQEV5R8l4PzNGs/DcrLzTC66FKxHHzyih4mlBF5mS
16
+ /sMY5l9QNaJHKPK7fKZ5yxohkda7tDwtb5p0bbEUQlMeZIQ8r/go1b7/ppXXM5ei
17
+ mTDcwKgeOBgVujqhvgGXOKtcHVfkrqcID+qgDMxYd7XXE/icGF3zmnHwDQ==
18
+ -----END CERTIFICATE-----
@@ -0,0 +1,16 @@
1
+ -----BEGIN CERTIFICATE REQUEST-----
2
+ MIICkDCCAXgCAQAwEjEQMA4GA1UEAwwHcmVsdWtrbzCCASIwDQYJKoZIhvcNAQEB
3
+ BQADggEPADCCAQoCggEBAKWOvLduU5YUlF2KfNwfhfNGe8qWGGh8GG1Gw8f6m+Oa
4
+ sc6/natePVd4epdoQF6+E2I3uU/l6DZSA6uzQoBVdhHY1r0zGuHwAJf4ip4QkX0E
5
+ qA7i3ZG8i0NTdJbFXd0hgxqwGF7+INcMopl3+NU6KW4v3O7Gj2W0NqBnFzvaNYye
6
+ +s6W+WdlUS0fif2L6GfHV1pZFkbzsOM7p3wjwXydfo1OomAGWtGbyOVyrY38bx4d
7
+ MWo6stjjGTgIX5pUTGkmsQamt+E2RsTTbm0khsY0z1e/5bSwj+eETAl2/iJ8Q/9v
8
+ 7jif4oesC6s5ugViZFyhn1WfgLUq5gG8M6mCTA6zXC0CAwEAAaA5MDcGCSqGSIb3
9
+ DQEJDjEqMCgwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMB
10
+ MA0GCSqGSIb3DQEBCwUAA4IBAQBfEyjmGQtITqSsmoAQXEb11Cm/olsL5w6kEzgr
11
+ /0K8ASvDvW+3yJ3U9OBngvgh6iQCJjYaixb8NtQS7yVWYEqJoaA0Hp1q0QDmRS/l
12
+ MDLe2cILgbLQUKOf1jLtYsLG93Lb74HSGztjIIEoi3Wgrm5LLGc/cd28q8yw5c/j
13
+ s1EntUHeNWCYAUIRvwmt+1UPcc8EBHH2DugGz2verzARdvLWAajL5r9ZIJkkU0kY
14
+ fh5gnmVb6I6RtIXhWcXMUL9nBwzPCyJMd4cC/fueC+HxzLNhGldvVxj3cg7W57vR
15
+ QnbG/qcJYN5WGhoHrWIiI90GufG4jifVvyQeBC/vQcymRlAL
16
+ -----END CERTIFICATE REQUEST-----
@@ -0,0 +1,28 @@
1
+ -----BEGIN PRIVATE KEY-----
2
+ MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCljry3blOWFJRd
3
+ inzcH4XzRnvKlhhofBhtRsPH+pvjmrHOv52rXj1XeHqXaEBevhNiN7lP5eg2UgOr
4
+ s0KAVXYR2Na9Mxrh8ACX+IqeEJF9BKgO4t2RvItDU3SWxV3dIYMasBhe/iDXDKKZ
5
+ d/jVOiluL9zuxo9ltDagZxc72jWMnvrOlvlnZVEtH4n9i+hnx1daWRZG87DjO6d8
6
+ I8F8nX6NTqJgBlrRm8jlcq2N/G8eHTFqOrLY4xk4CF+aVExpJrEGprfhNkbE025t
7
+ JIbGNM9Xv+W0sI/nhEwJdv4ifEP/b+44n+KHrAurOboFYmRcoZ9Vn4C1KuYBvDOp
8
+ gkwOs1wtAgMBAAECggEAAlGzzikAoqYfc7wywBDXLkXjClhpN2t6zYStTbhlvyQ4
9
+ 9rgdJA9CC9JI+1gVqlrkyB5j0IOPg27i+y1PBblMwF0mf7t+/KiOEl+3R2J2VODZ
10
+ q6Dp110KB8bCdLGg/TqqZld02/D/S98ZY83eezqUZbH/cqVu6rVbaSQdWyeh7Bow
11
+ 0rfnMV2537JpZVXzmVDGhlhKcgKjJFkzdW4Zv34lMSOCAAeByrIJtPEoQrJ7MQOj
12
+ 9WpwYN+JN4YeP1FWXIG3Ah7ivFsCMZN9MEvXWMcEnAqSHhXXyRzJnJh4jBrigfM1
13
+ JeqDWgiMlCLP5Z2CwA8ahTnJZAYBJUfeFiNHBYC1sQKBgQDjoTCIlCr7UyLkxaJr
14
+ +IY65tl0jjWiEEvSZ9E+fqqesAsABGE8Po1ZEA7VZNViFf+4ttuBZLngmm/YVBvV
15
+ Ok+hf1HSDsvNhon7oIIa7cxlYGKH2Lf6tq8ovtt7IXRknInbbxh6+CoMm5Yc1KXp
16
+ XRCApfeBEoQdM2N2TRLUL+pPkQKBgQC6MQ9L3zMEr2yXrkyi2wmqBMMaSHCM5Zuh
17
+ ifQSFjJfkarh2y0KN7I73HR6KJVMVRVSmARi/QgN8tEvaXjCvVN67re4mDiZ5rC+
18
+ uIx903tkuKR4ruC1DE96NtsM1VNh1mUa2ndPULztpqPXhMCPspMltH4KbJHLo/iv
19
+ MaRHu2Ps3QKBgDGluIYxeuZyDYxEJtbRz8UNEKTBV6VPVJpBlnvA+Mm/r8aDLgoL
20
+ wpyIShP0+C16itno2Btysq/wPzC5t5BcNX4N3PSp0X20OGgva/EfqJL74MGsXQRL
21
+ 0AxdBFXoyBAhrFe19WrssY8qaCVFpCIv8Cd/C2qVdUdBLBJjYRXylXJhAoGBAIPX
22
+ z5QTReYwbj3uD5hWcD5zRtBqsUAkKpM1w523GA5ycMWj1P3iOJSI4/XInQfDS+kD
23
+ ej+6GyT9Z0Eid3+2XtGzDmdpatMX9tnmhp1mzn6g/N30P2wUfob1JyGsQXILRebB
24
+ AjEo48+I64XmHBDlC6tsZr/s50SDJMqx5TliKT0FAoGAeqqC57jD2bj8Srs1Xa9p
25
+ JDZdS1DU/HthIngHTlud/rvOs2koVbIKRAHqxoBn2TCii6srFf7nHgLZ4iDOiyHF
26
+ eOb52GaYYKQNiSF77LUV0wdxmwwhm3h6hRYgt52nT1JVHH6rilZ64e2mevPX5H1T
27
+ CVvlmUZTRqXOidoSGMkO3sQ=
28
+ -----END PRIVATE KEY-----
@@ -0,0 +1,19 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDGzCCAgOgAwIBAgIURXOBbBP+BSnq15fqso3+0Wkwt2swDQYJKoZIhvcNAQEL
3
+ BQAwFTETMBEGA1UEAwwKUmVsdWtrbyBDQTAeFw0yNDExMTYyMTM4MDVaFw00NDEx
4
+ MTEyMTM4MDVaMBUxEzARBgNVBAMMClJlbHVra28gQ0EwggEiMA0GCSqGSIb3DQEB
5
+ AQUAA4IBDwAwggEKAoIBAQDSDxl9hWmwLjTwJTHTH55HwNFKDMHXjiMmig2SMVCE
6
+ Y9fRYNms46CU+ulGGr8TMqXExb3J1ywE/HB8PBYoPKvAmPBHrquXT03A64fXl30q
7
+ IcnRHsJymxmUQV7yQxyfIXHT57NGUBauRhYSG8enbTNPbwByAdDAK6z0/41mMPbz
8
+ NFSXR89+ZkYf0CfKkvi/lPGdC80rS9LokLvDniVT50ir4CurhzndBP/U82nTMxvr
9
+ +Q2/TQmxbzeHOpa4ZCD2OBAHNXAGgV1qltnhvmbn+zYEK+vt9ZSb6lRUXWBWUvkn
10
+ TRCy3lMHu27q5h8Gzzxvi0Ct2lJ02poZG98QlXoAto+RAgMBAAGjYzBhMB0GA1Ud
11
+ DgQWBBT+sHazfmyczKb0BV6rZ1JQ8Sfx9zAfBgNVHSMEGDAWgBT+sHazfmyczKb0
12
+ BV6rZ1JQ8Sfx9zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkq
13
+ hkiG9w0BAQsFAAOCAQEAASuRdyC8zk+FD3DO8vO3bpxiewqjSjQ0cE/t2XU3floV
14
+ r6Lp1bS+IjpeKIYzTg3+DdIMGub8piCh7JcU5ru1Ef0/xwtmCCUB/rOcYwfa2nHj
15
+ w7sc+njSnmdpISCyUV9Yxsqs+b2eQOMj/D28R5c2vf7QIYQMJivqa44TrMy57mJP
16
+ Tu+Y4stHZnfrnlggScfjn+dKjT58awyI0UVPr80YWZuSIDx26CREP41X8SJ5qRMN
17
+ K6KFXxN/PJhl/Y5axV/k/omRXd1FusUaC8yGk5E4zCs7cvbhG830FVIM19zbVSKg
18
+ SF6Dvx3mEWraHyBls9e+FYQIxB5FEJz9KGZdsOSjcQ==
19
+ -----END CERTIFICATE-----
Binary file
@@ -0,0 +1,28 @@
1
+ -----BEGIN PRIVATE KEY-----
2
+ MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDSDxl9hWmwLjTw
3
+ JTHTH55HwNFKDMHXjiMmig2SMVCEY9fRYNms46CU+ulGGr8TMqXExb3J1ywE/HB8
4
+ PBYoPKvAmPBHrquXT03A64fXl30qIcnRHsJymxmUQV7yQxyfIXHT57NGUBauRhYS
5
+ G8enbTNPbwByAdDAK6z0/41mMPbzNFSXR89+ZkYf0CfKkvi/lPGdC80rS9LokLvD
6
+ niVT50ir4CurhzndBP/U82nTMxvr+Q2/TQmxbzeHOpa4ZCD2OBAHNXAGgV1qltnh
7
+ vmbn+zYEK+vt9ZSb6lRUXWBWUvknTRCy3lMHu27q5h8Gzzxvi0Ct2lJ02poZG98Q
8
+ lXoAto+RAgMBAAECggEAMnh7TOv5xWGMyah+LLZ7nszm3G5tQr/9lRx1f0tHB9A/
9
+ gijNlFtK8ZDJ7NSvVUsFhl5mp/EBvB71XfxH7JoLQGbe0LJYuy3MRk6xIG1fHvjo
10
+ V00XNlcq0bLVP8s9PMTZ5yrUKdBDatMfKXGwSnuT6QGIL1xdTlcQIvul+0hHhY58
11
+ +QGhB1HJASChg13OjLFwDhjiUqH1rkr8WjFspZZIm+SrwXUZTr6PYkF4E1cBcsWT
12
+ 4PLCYKTZTKKr3D0UVa5ipOkp/ZIgWqRk1SACn84G1/AkmBpgv+0XgZz8kZ0Ju+9W
13
+ sPQV/fxXYpvnM/cvkHBAnvWDp4N3VX648zez7t+f2wKBgQD7guX7G5LdtLB/Crlx
14
+ uyAhkJyvc8Ttt11HCf2T/k6oqhATt9014NlJYQY2pVlyoyEQEb1Pe919ei+UpVVt
15
+ Ahaanw5xveOeGdjc2YJIqQV4mqc/daEXFfyLU+3dCzTKB4AXaIT09If5mna46Ou/
16
+ q1UW96cH/SqLC5Z3CyUhqwIvfwKBgQDVztB2P1KBH45lOT1oP/vPxYklUSokA/g+
17
+ VRw3pB8Wmm86fSxQ1lEvMViVf/tyrc+dpvNS+7quNzc0U/2+vpMcLjBkS9EpxAMy
18
+ hPDwZPnPXHKHnC/ET9WWO+wVaCTienGSVaTDTToeKo71Oesi1etHkLl0aWEMp8vo
19
+ gYGPM+/I7wKBgF0dNhXEPJhWF+3Aw60mcRvcFRYjzymO9DqjqTzMLsTkyTTzojMb
20
+ PnabNBixGK+HU2tGlgbCPdsBjHIEz96eok22Szuy6H0U5qPE5yYNZVgkMX0eCHiq
21
+ jCfs96gZSP7uze5cpLSnf1vaYs/Mc9MvFD5/i1g60zBNJvSFrMaNMC7zAoGBANNp
22
+ y+GQ3aEGNgRLZDgIRJN/DQyURCoWrsnb2KoAJClyjZd1HHPWZ7JVhfVV9qm/yjJm
23
+ WeXtOft4Z6LbhmWtBcPwvDMAcRNuWYcDw0bBYe6zyB/lxanFEzqZh+jm8MntLw32
24
+ l5PycgxQIsnC+omYoyxBo8/DP46HAO6n2Bdk0K6LAoGACP/UfrAg4Hyqi0jJcusl
25
+ Led0R3Fl0dw5i2N9PB++PUajt5LFhXXGHeOQnTlYbeJ/ioJgRS/k6W7MH43zP0me
26
+ wIYrVmazCewXwcIZiRv3ees73OsAVrn/yl17+u4KkVlXi0me8Opljl4/MS8026rE
27
+ iKMsJnco7XH/vaS9MrrrOvE=
28
+ -----END PRIVATE KEY-----
@@ -0,0 +1 @@
1
+ 5C540DF8038EECA15D8953551666A7F012E445F9
@@ -0,0 +1,79 @@
1
+ import os
2
+ import socket
3
+ import ssl
4
+ import threading
5
+ from pathlib import Path
6
+ from typing import List
7
+
8
+ import pytest
9
+ from testcontainers.core.network import Network
10
+
11
+ from pyrelukko import RelukkoClient
12
+ from pyrelukko.testcontainers import RelukkoContainer, RelukkoDbContainer
13
+
14
+ SCRIPT_DIR = Path(__file__).parent.absolute()
15
+
16
+
17
+ @pytest.fixture(scope="session")
18
+ def relukko_backend():
19
+ if os.environ.get("CI_HAS_RELUKKO"):
20
+ # Gitlab does not allow DinD networks, so Relukko runs as a service
21
+ # in the background of the job.
22
+ relukko = RelukkoClient(
23
+ base_url=os.environ['CI_RELUKKO_BASE_URL'],
24
+ api_key=os.environ['CI_RELUKKO_API_KEY']
25
+ )
26
+ yield relukko, None
27
+ else:
28
+ with Network() as rl_net:
29
+ with RelukkoDbContainer(net=rl_net,
30
+ image="postgres:16", hostname="relukkodb") as _db:
31
+ db_url = "postgresql://relukko:relukko@relukkodb/relukko"
32
+ with RelukkoContainer(rl_net, db_url=db_url) as backend:
33
+ relukko = RelukkoClient(
34
+ base_url=backend.get_api_url(), api_key="somekey")
35
+ yield relukko, backend
36
+
37
+ @pytest.fixture(scope="function")
38
+ def tls_listener():
39
+
40
+ certfile = SCRIPT_DIR / "cert" / "relukko.crt"
41
+ keyfile = SCRIPT_DIR / "cert" / "relukko.key"
42
+
43
+ def run_server(port_info: List, keep_running: threading.Event):
44
+ # Create a TCP socket
45
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock:
46
+ sock.bind(("127.0.0.1", 0))
47
+ sock.listen(5) # Listen for incoming connections
48
+
49
+ _, assigned_port = sock.getsockname()
50
+ port_info.append(assigned_port)
51
+
52
+ # Wrap the socket with TLS
53
+ context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
54
+ context.load_cert_chain(certfile=certfile, keyfile=keyfile)
55
+ with context.wrap_socket(sock, server_side=True) as ssock:
56
+
57
+ ssock.settimeout(1)
58
+
59
+ while keep_running.is_set():
60
+ try:
61
+ client_socket, _ = ssock.accept()
62
+ client_socket.sendall(b"Hello, TLS Client!")
63
+ client_socket.close()
64
+ except Exception as _:
65
+ continue
66
+
67
+ port_info = []
68
+ keep_running = threading.Event()
69
+ keep_running.set()
70
+ thread = threading.Thread(target=run_server, args=(port_info, keep_running))
71
+ thread.start()
72
+
73
+ while not port_info:
74
+ pass
75
+
76
+ yield thread, port_info[0]
77
+
78
+ keep_running.clear()
79
+ thread.join()
@@ -2,7 +2,7 @@ import logging
2
2
  import time
3
3
  import ssl
4
4
  import socket
5
- from datetime import datetime, timezone
5
+ from datetime import datetime, timedelta, timezone
6
6
  from pathlib import Path
7
7
  from typing import Dict
8
8
 
@@ -32,7 +32,7 @@ def _check_tls(ssl_ctx: ssl.SSLContext, port):
32
32
 
33
33
  def _check_has_serial_no(ssl_ctx: ssl.SSLContext):
34
34
  serial_numbers = [x["serialNumber"] for x in ssl_ctx.get_ca_certs()]
35
- assert "78B4E1E498136651B475BC2D0880A28E4835316C" in serial_numbers
35
+ assert "4573816C13FE0529EAD797EAB28DFED16930B76B" in serial_numbers
36
36
 
37
37
 
38
38
  def test_init_relukko_client():
@@ -59,6 +59,11 @@ def test_init_relukko_client():
59
59
  assert relukko.backoff == 2.0
60
60
  assert relukko.max_delay is None
61
61
  assert relukko.exceptions == (requests.ConnectionError, RelukkoDoRetry)
62
+ assert relukko.ws_wait_for_timeout == 2
63
+ assert relukko.ws_ping_interval == 60
64
+ assert relukko.ws_ping_timeout == 20
65
+ assert relukko.acquire_wait_for_timeout == 2
66
+ assert relukko.acquire_modulo == 100
62
67
 
63
68
  with pytest.raises(ValueError):
64
69
  RelukkoClient(base_url=1000, api_key="")
@@ -152,7 +157,7 @@ def test_reconfigure_relukko_client():
152
157
  "https://relukko", api_key="secret-key")
153
158
 
154
159
  assert relukko.base_url == parse_url("https://relukko")
155
- assert relukko.ws_url == parse_url("wss://relukko/deletions")
160
+ assert relukko.ws_url == parse_url("wss://relukko/ws/broadcast")
156
161
  assert relukko.session.headers['X-api-Key'] == "secret-key"
157
162
 
158
163
  default_ctx = ssl.create_default_context()
@@ -169,6 +174,11 @@ def test_reconfigure_relukko_client():
169
174
  assert relukko.backoff == 2.0
170
175
  assert relukko.max_delay is None
171
176
  assert relukko.exceptions == (requests.ConnectionError, RelukkoDoRetry)
177
+ assert relukko.ws_wait_for_timeout == 2
178
+ assert relukko.ws_ping_interval == 60
179
+ assert relukko.ws_ping_timeout == 20
180
+ assert relukko.acquire_wait_for_timeout == 2
181
+ assert relukko.acquire_modulo == 100
172
182
 
173
183
 
174
184
  def test_reconfigure_relukko_client_extended():
@@ -178,6 +188,11 @@ def test_reconfigure_relukko_client_extended():
178
188
  verify_mode = ssl.VerifyMode.CERT_NONE
179
189
  verify_flags = ssl.VerifyFlags.VERIFY_DEFAULT
180
190
  options = ssl.Options.OP_ALL
191
+ ws_wait_for_timeout = 27
192
+ ws_ping_interval = 600
193
+ ws_ping_timeout = 23
194
+ acquire_wait_for_timeout = 22
195
+ acquire_modulo = 202
181
196
 
182
197
  relukko.reconfigure_relukko(
183
198
  base_url="https://relukko", api_key="my-API-key", trust_env=False,
@@ -185,13 +200,16 @@ def test_reconfigure_relukko_client_extended():
185
200
  status=96, other=95, backoff_factor=94, backoff_max=1000,
186
201
  backoff_jitter=93, raise_on_redirect=False, raise_on_status=False,
187
202
  check_hostname=False, hostname_checks_common_name=False,
188
- verify_mode=verify_mode, verify_flags=verify_flags, options=options
203
+ verify_mode=verify_mode, verify_flags=verify_flags, options=options,
204
+ ws_wait_for_timeout=ws_wait_for_timeout, acquire_modulo=acquire_modulo,
205
+ ws_ping_interval=ws_ping_interval, ws_ping_timeout=ws_ping_timeout,
206
+ acquire_wait_for_timeout=acquire_wait_for_timeout,
189
207
  )
190
208
 
191
209
  assert relukko.session.trust_env == False
192
210
  assert relukko.session.cookies == cookies
193
211
  assert relukko.base_url == parse_url("https://relukko")
194
- assert relukko.ws_url == parse_url("wss://relukko/deletions")
212
+ assert relukko.ws_url == parse_url("wss://relukko/ws/broadcast")
195
213
  assert relukko.session.headers['X-api-Key'] == "my-API-key"
196
214
  http_adapter: HTTPAdapter = relukko.session.adapters.get("http://")
197
215
  assert http_adapter.max_retries.total == 100
@@ -218,22 +236,35 @@ def test_reconfigure_relukko_client_extended():
218
236
  assert relukko.backoff == 2.0
219
237
  assert relukko.max_delay is None
220
238
  assert relukko.exceptions == (requests.ConnectionError, RelukkoDoRetry)
239
+ assert relukko.ws_wait_for_timeout == ws_wait_for_timeout
240
+ assert relukko.ws_ping_interval == ws_ping_interval
241
+ assert relukko.ws_ping_timeout == ws_ping_timeout
242
+ assert relukko.acquire_wait_for_timeout == acquire_wait_for_timeout
243
+ assert relukko.acquire_modulo == acquire_modulo
221
244
 
222
245
 
223
246
  def test_init_relukko_client_extented():
224
247
  cookies = cookiejar_from_dict({"cookie1": "value1"})
248
+ ws_wait_for_timeout = 27
249
+ ws_ping_interval = 600
250
+ ws_ping_timeout = 23
251
+ acquire_wait_for_timeout = 22
252
+ acquire_modulo = 202
225
253
 
226
254
  relukko = RelukkoClient(
227
255
  base_url="http://relukko", api_key="my-API-key", trust_env=False,
228
256
  cookies=cookies, total=100, connect=99, read=98, redirect=97,
229
257
  status=96, other=95, backoff_factor=94, backoff_max=1000,
230
258
  backoff_jitter=93, raise_on_redirect=False, raise_on_status=False,
259
+ ws_wait_for_timeout=ws_wait_for_timeout, acquire_modulo=acquire_modulo,
260
+ ws_ping_interval=ws_ping_interval, ws_ping_timeout=ws_ping_timeout,
261
+ acquire_wait_for_timeout=acquire_wait_for_timeout,
231
262
  )
232
263
 
233
264
  assert relukko.session.trust_env == False
234
265
  assert relukko.session.cookies == cookies
235
266
  assert relukko.base_url == parse_url("http://relukko")
236
- assert relukko.ws_url == parse_url("ws://relukko/deletions")
267
+ assert relukko.ws_url == parse_url("ws://relukko/ws/broadcast")
237
268
  assert relukko.session.headers['X-api-Key'] == "my-API-key"
238
269
  assert relukko.ssl_ctx is None
239
270
  http_adapter: HTTPAdapter = relukko.session.adapters.get("http://")
@@ -253,6 +284,11 @@ def test_init_relukko_client_extented():
253
284
  assert relukko.backoff == 2.0
254
285
  assert relukko.max_delay is None
255
286
  assert relukko.exceptions == (requests.ConnectionError, RelukkoDoRetry)
287
+ assert relukko.ws_wait_for_timeout == ws_wait_for_timeout
288
+ assert relukko.ws_ping_interval == ws_ping_interval
289
+ assert relukko.ws_ping_timeout == ws_ping_timeout
290
+ assert relukko.acquire_wait_for_timeout == acquire_wait_for_timeout
291
+ assert relukko.acquire_modulo == acquire_modulo
256
292
 
257
293
 
258
294
  def test_init_relukko_client_ssl_ctx():
@@ -351,7 +387,25 @@ def test_acquire_relukko(relukko_backend):
351
387
  lock = relukko.acquire_relukko("pylock", "pytest", 30)
352
388
  end_time = time.time()
353
389
  assert lock is None
354
- assert 29 < end_time - start_time < 34
390
+ assert 29 < end_time - start_time < 45
391
+
392
+
393
+ def test_acquire_relukko_with_del(relukko_backend):
394
+ relukko, _ = relukko_backend
395
+ lock = relukko.acquire_relukko("Pylock", "pytest", 10)
396
+ id = lock['id']
397
+
398
+ expires_at = datetime.now(timezone.utc) - timedelta(minutes=2, seconds=45)
399
+ upd_lock = relukko.update_relukko(id, expires_at=expires_at)
400
+ assert id == upd_lock['id']
401
+
402
+ start_time = time.time()
403
+ lock = relukko.acquire_relukko("Pylock", "pytest", 90)
404
+ end_time = time.time()
405
+
406
+ assert lock is not None
407
+ assert upd_lock['id'] != lock['id']
408
+ assert 29 < end_time - start_time < 50
355
409
 
356
410
 
357
411
  def test_delete_relukko(relukko_backend):
@@ -1,9 +0,0 @@
1
- [tool.pylint.main]
2
- # Files or directories to be skipped. They should be base names, not paths.
3
- ignore = ["LICENSE", "pyproject.toml", "version.py",
4
- "README.md", "demo.py", "conftest.py"]
5
-
6
- # Files or directories matching the regular expression patterns are skipped. The
7
- # regex matches against base names, not paths. The default value ignores Emacs
8
- # file locks
9
- ignore-patterns = ["^\\.#", "^test_"]
@@ -1,39 +0,0 @@
1
- [project]
2
- name = "pyrelukko"
3
- authors = [
4
- {name = "Reto Zingg", email = "g.d0b3rm4n@gmail.com"},
5
- ]
6
- readme = "README.md"
7
-
8
- # https://pypi.org/pypi?%3Aaction=list_classifiers
9
- classifiers = [
10
- "Development Status :: 3 - Alpha",
11
- "Intended Audience :: Developers",
12
- "License :: OSI Approved :: MIT License",
13
- "Topic :: Internet :: WWW/HTTP",
14
- "Topic :: Software Development :: Libraries :: Python Modules",
15
- ]
16
- requires-python = ">=3.12"
17
- dynamic = ["version", "description"]
18
- dependencies = [
19
- "requests >=2.32.3",
20
- ]
21
-
22
- [tool.flit.sdist]
23
- exclude = [".gitignore", "demo.py"]
24
-
25
- [project.urls]
26
- Homepage = "https://gitlab.com/relukko/pyrelukko"
27
- Issues = "https://gitlab.com/relukko/pyrelukko/-/issues"
28
-
29
- [build-system]
30
- requires = ["flit_core >=3.2,<4", "semantic-version >= 2.10"]
31
- build-backend = "flit_core.buildapi"
32
-
33
- [tool.pytest.ini_options]
34
- testpaths = [
35
- "tests",
36
- ]
37
- pythonpath = [
38
- "src",
39
- ]
@@ -1,8 +0,0 @@
1
- # How To
2
- ```
3
- openssl req -x509 -sha256 -days 7300 -noenc -newkey rsa:2048 -keyout rootCA.key -out rootCA.crt
4
- ln -s rootCA.crt "$(openssl x509 -hash -noout -in rootCA.crt)"
5
- openssl req -newkey rsa:2048 -noenc -keyout relukko.key -out relukko.csr
6
- openssl x509 -req -CA rootCA.crt -CAkey rootCA.key -in relukko.csr -out relukko.crt -days 7299 -CAcreateserial
7
- openssl x509 -inform PEM -in rootCA.crt -outform DER -out rootCA.der
8
- ```
@@ -1,21 +0,0 @@
1
- -----BEGIN CERTIFICATE-----
2
- MIIDeDCCAmCgAwIBAgIUIG2mO1IxfM3wqiLLFCO+6ubGdHswDQYJKoZIhvcNAQEL
3
- BQAwVDELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE
4
- CgwTRGVmYXVsdCBDb21wYW55IEx0ZDEQMA4GA1UEAwwHUmVsdWtrbzAeFw0yNDEx
5
- MTMxNTQxNTdaFw00NDExMDcxNTQxNTdaMFQxCzAJBgNVBAYTAlhYMRUwEwYDVQQH
6
- DAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQxEDAO
7
- BgNVBAMMB3JlbHVra28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCr
8
- 89++zNQs+MG5opoCTWq/QxAJsr9Uul598XoQFXn1N/d/JOeSosy8BNvb1RXsr5sp
9
- sNJbJEun6OPFtUoVczUtivS99XNchWPjEQ18w22aiLcwl/uOrdfxWj04yEfKmejN
10
- ZXFrShhAWRGfc3fi2hD0Y5mtbS7T5iqaYzm4U5UDVFizVPnSgPFXMAiat/VQGvBF
11
- cv5IpBjXOJGkLZTzmFlW61T6m+MJGXF59CLC6vL6QJCqQpSUWG9UM00R8L2pQ1b8
12
- HLKYUuJ+vp4WVYwYyz769BNG4OG3IkdqIHKDFWBXFVmKaUkX9aAul4BOitKy4XFc
13
- 8eu653lbkVnRHb01RaP7AgMBAAGjQjBAMB0GA1UdDgQWBBSEFybZBM4O7khSofZl
14
- 0oAT+CSXEjAfBgNVHSMEGDAWgBRAo64Da82emF0q53wCdj4wbFTmMDANBgkqhkiG
15
- 9w0BAQsFAAOCAQEAs+zaKt8VqLoVT0OT24Oxyzc+/A0h4BYq3kYD11BaUJ6oKqQu
16
- 57FRKi0ZWxNSNCbzrwi6W+qNgIXtvSHWk4+BKxzqDVG6LCIuNn4bbvlQMGuTWctY
17
- ndArPxdKvN9AXeCvam+5XKcA5OJY/naoho7fSoGh2Zgkq9HS7DlKxfBW2GEY0mp8
18
- jeuxvBvh4xw/YuESVFbKedglaoNFf+aP44zL47Wn9QJcLqByGpTV472j238ObUFi
19
- k47o+v4Qt7nkJDOcx7ic//uLTcb/1rv1SppYpKr3YJ5fLspzMMPpTQbkt2H8hLiZ
20
- +1jj5GIoDgLi+/uxBgYQs78jEOIKkWO6EjwGUw==
21
- -----END CERTIFICATE-----
@@ -1,16 +0,0 @@
1
- -----BEGIN CERTIFICATE REQUEST-----
2
- MIICmTCCAYECAQAwVDELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0
3
- eTEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDEQMA4GA1UEAwwHcmVsdWtr
4
- bzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKvz377M1Cz4wbmimgJN
5
- ar9DEAmyv1S6Xn3xehAVefU3938k55KizLwE29vVFeyvmymw0lskS6fo48W1ShVz
6
- NS2K9L31c1yFY+MRDXzDbZqItzCX+46t1/FaPTjIR8qZ6M1lcWtKGEBZEZ9zd+La
7
- EPRjma1tLtPmKppjObhTlQNUWLNU+dKA8VcwCJq39VAa8EVy/kikGNc4kaQtlPOY
8
- WVbrVPqb4wkZcXn0IsLq8vpAkKpClJRYb1QzTRHwvalDVvwcsphS4n6+nhZVjBjL
9
- Pvr0E0bg4bciR2ogcoMVYFcVWYppSRf1oC6XgE6K0rLhcVzx67rneVuRWdEdvTVF
10
- o/sCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQCHkmw8bBBI9pOrlCrJ2u3sGdlx
11
- 3ZbW/+kGdsQ8Odb/pIHkLLKpu5yNLEey/U4EZyWSNewYDUKfS2FIyMN2c38QAex6
12
- 1Y9qPK74RzUB68ki7XKtp+GAGqVb0axxRe0IuBwc9d5jl6zjltcpdutAIIgXcJgg
13
- 8QPnfpz7Skhjjm/duTVp0rurWsPztQdDpkrcY3c49vuTLA5uKwfa//cPAOmVGwqq
14
- TMICj+0M2S+bgg9Ksph1IsDxxuIm1aa2EFnF5XRbJn9aOSdyfBkKoZz9ttgu1hP4
15
- +hPe8S3ekUyJiZAglSI1qJOkpyw14EDIhwJvE9FG5eN/mU8wS1/GU+vhZWKN
16
- -----END CERTIFICATE REQUEST-----
Binary file
@@ -1,28 +0,0 @@
1
- -----BEGIN PRIVATE KEY-----
2
- MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCr89++zNQs+MG5
3
- opoCTWq/QxAJsr9Uul598XoQFXn1N/d/JOeSosy8BNvb1RXsr5spsNJbJEun6OPF
4
- tUoVczUtivS99XNchWPjEQ18w22aiLcwl/uOrdfxWj04yEfKmejNZXFrShhAWRGf
5
- c3fi2hD0Y5mtbS7T5iqaYzm4U5UDVFizVPnSgPFXMAiat/VQGvBFcv5IpBjXOJGk
6
- LZTzmFlW61T6m+MJGXF59CLC6vL6QJCqQpSUWG9UM00R8L2pQ1b8HLKYUuJ+vp4W
7
- VYwYyz769BNG4OG3IkdqIHKDFWBXFVmKaUkX9aAul4BOitKy4XFc8eu653lbkVnR
8
- Hb01RaP7AgMBAAECggEAF22KShjmkwavf7HfnDmyYMMWhSO//XTJBA1/DSrylg5d
9
- NQsGVsxslZ9vLyP4sT9PJjPWbUmO2fPuo1TVy6y0jZ3K8Qi8rNnrEphpBzB/Qe38
10
- +5dxfu6WUebIGZucFca42SdITTm9VSMYNhU0O98XLp0AEV4jYlvlPmoPhrAzSM50
11
- lEIuriNhqHuqZhXN0jPYI1jZDS5JVi2PlbgyO1125BBMsCMLfhW+wcme/Y/VAySc
12
- pNkvK3aHeWJUXEeVKuNfn5878PqeD6ZPjl9Hyq3lj6yOauIO/PCdIwm+2qjzrYsy
13
- IBhjFFEoMGofgz+KV7olZu9by38ZzhW7nZGGe+zbgQKBgQDmFyzzxz3/BF7FKTXL
14
- v6uVNkGpMGH8EQE1H+YJXTiSaqf/3ZrsXyBsbA6YBanKBXcY/F0PhQ7e/rjtqaGU
15
- OtekNTHSgosdbJgVX88EjfIa4/rbZPUsX6qRJBuDVOCcCZC1PyFBT2ehKYlIPHS0
16
- klbXo57pYzgDnzrYPr6UWbCZewKBgQC/UMGNvqJXszYZe3hjd9Ek9oSJMThfpfXn
17
- opNhCNHKZ8+xqry602TqIwyStYc7elB57R547ZR4kttT8iM1E7ZOoLRPP0r/FjOS
18
- scpCAk5ouVG9RZaUvTIbvzdEifEOt1Vf0RgIG3Y4DBqtotNoymqyT5wzuxyJQm1E
19
- 683UVIbXgQKBgQCLPJ0UTI7kwtVCxIRsbum7WuDzLHcvHW84obwIEKSKXgaaHJWC
20
- 0rIBSoauUkcEHLiMozMBkEiGg2iPUaaY197k3NfwhtT+kleaH6dcHzXSNgH5QCfp
21
- mV7ThCEuIW/mnRc3xyMtrYqNiWAtGYCaQTBSQA6LN2KPNo1ajOWxSnFG/wKBgQC2
22
- 9rBksq/nV4ihjidwWSI3S1stKVlUgA9QW3a/EgQwol9K9pJPyeN019gqZljSVQOp
23
- 10+RLwUS2r/O5H8vP47WW3KVZ1593emsnUNlJXd/R9wYOvjrfpTxXEmqzpEvFb4c
24
- SIfHGRxSNaE99b5hNVQc+23TO1rrGhAOHcVXDw92AQKBgQDCMEcIcwHx+ahVZbEb
25
- bAyWLwYxQFRStmYKbYBtEMFW39atRmc0wdAG0my/0cUyHUIfWbHvP5oTfyAthV9G
26
- 8G0qBk5fPhh6hfNRFMb2j1AqnZ3CadoBZq2HelTTUqt4UgU9Y4iDco+4ew34Uw76
27
- 2ki1bKXiJKkJYlk14AMrzckDUQ==
28
- -----END PRIVATE KEY-----
@@ -1,21 +0,0 @@
1
- -----BEGIN CERTIFICATE-----
2
- MIIDiTCCAnGgAwIBAgIUeLTh5JgTZlG0dbwtCICijkg1MWwwDQYJKoZIhvcNAQEL
3
- BQAwVDELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE
4
- CgwTRGVmYXVsdCBDb21wYW55IEx0ZDEQMA4GA1UEAwwHUmVsdWtrbzAeFw0yNDEx
5
- MTMxNTM3MDBaFw00NDExMDgxNTM3MDBaMFQxCzAJBgNVBAYTAlhYMRUwEwYDVQQH
6
- DAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQxEDAO
7
- BgNVBAMMB1JlbHVra28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD4
8
- PWQPjCCXOvZYUDpYhZHR4Nw0ErkZEZWtPMyoBnyGkYwqK1SrHCcIYQL8C22dN1rn
9
- VdFvCVH13fQHz1SJ/v3jkS+9IiOUDenXpZP/G0pprTbLGh33aaQU53EqvO1T2CAA
10
- zIAVdme0cihOB+KL2A5bDzP9fu+c0ddn+N++ZgcqBHMYmZKIGJv5QZ+i+/1xrGqQ
11
- /znwV7RuBGms8fxA3x4Y4ELUzIHMpbXbQn95qC9u7ee4odQ5KjR1M0oocPKOYBGa
12
- /Dqnhd3Wx0lHR3No8HgC4TBfxruhs+NZtRyInOTFr99TWbBx+zrDvxecM/5j/AnH
13
- WHka8xMQSxJUeoXtlFT/AgMBAAGjUzBRMB0GA1UdDgQWBBRAo64Da82emF0q53wC
14
- dj4wbFTmMDAfBgNVHSMEGDAWgBRAo64Da82emF0q53wCdj4wbFTmMDAPBgNVHRMB
15
- Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQACdVeJ/CnXpY4t568bQRxIr+t3
16
- IqeIqJVg1nt6OaB/EJPJil/lvJD8bjO5W5uXAnbkv26i0Q89KFZH3xKFHRmL/zR+
17
- 0YPcbi3LmQ+NY6LtH1GtBlRiobROfSp8/GxTrGxqMGHHxBdY+v8lPiK1LHTE99ik
18
- cl8qF+DQMGeliPQcg9rLgXagymEc9oS3OgxjL7Al4/P8bLkdC/hxk+cEUFCXUX4v
19
- z/PAFnHwEeJPiiNYG+hpQQRloVncynEwhBr/pGGMdLGaFBje41kn9Ss2anc+YaiW
20
- DyoDLuQkCcRrUvc2dCGMZs0KaCv8i61QWGNy8suGq6nrXW9A57JhS7ak8xg9
21
- -----END CERTIFICATE-----
Binary file
@@ -1,28 +0,0 @@
1
- -----BEGIN PRIVATE KEY-----
2
- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD4PWQPjCCXOvZY
3
- UDpYhZHR4Nw0ErkZEZWtPMyoBnyGkYwqK1SrHCcIYQL8C22dN1rnVdFvCVH13fQH
4
- z1SJ/v3jkS+9IiOUDenXpZP/G0pprTbLGh33aaQU53EqvO1T2CAAzIAVdme0cihO
5
- B+KL2A5bDzP9fu+c0ddn+N++ZgcqBHMYmZKIGJv5QZ+i+/1xrGqQ/znwV7RuBGms
6
- 8fxA3x4Y4ELUzIHMpbXbQn95qC9u7ee4odQ5KjR1M0oocPKOYBGa/Dqnhd3Wx0lH
7
- R3No8HgC4TBfxruhs+NZtRyInOTFr99TWbBx+zrDvxecM/5j/AnHWHka8xMQSxJU
8
- eoXtlFT/AgMBAAECggEAeQhprIDsdP/dE4MOZ5G8XsHNa6BDWlsuCLivqi77cRsu
9
- 5XKaBhGkYFv4eg5SZAiUzF0Vz5cSPJf6vwISHHRUsqtbxNIL2ciM3sVO6t+SAQNg
10
- x1cVDEgYJc8QaL+T0UD7nZgXzR8dgif0ydLjXL67hR9dAHhu+J2BcqKv6KPTU2LB
11
- KWbCcLkY4PdCGwSNuvyPokc3Fo8U40Lm8tcWQ6sFV/+bv6+ShWhsb5Dx7sfVoijf
12
- YikzFc7sEHb//nH2+C/FoCqneDPYl8lVH9zKYis4xWcA68bNwyfWpaQLXo32DKYq
13
- rZxnwnawtU5b+t+TVp1DcCjOM3vaodeKI3Gqq5SjBQKBgQD/Sy33WOK+qelkvJxb
14
- IcyP3U/ijNpWP3UidYGmmpTBP5UdaXyObX1G4nmfQjDad080jPpP/r350Tr0Xi2s
15
- 6wZBw0HoTZrZw5MQQKRQyh0Hg5VHHoHbD97UuLGGZ+Yc/hSWWfuDQHeV5Ami+VXD
16
- TCYZGhtsjY9lUfUWYcsdcOA1fQKBgQD47TcVSC+3/8zKd2AAJeYrpx6wL/5X/4Zn
17
- U0VeBxfF6MjQlu3avnwi6MxjkuB4KJAgWVHvJspJaS8ilIJN5qBsivphtfD0RV+d
18
- UW9DCzoDUlU/AmNMD+stylgziSWoG9rzcY7P71izRE1tGGsgOXvZS5xUHzqWBwg6
19
- MtYB4EQNKwKBgQCTUBVpKmBE9xTXbUKoD5vT1Df5mZ+PrzRvOvEiawa1cHQiMbGP
20
- Gjz0/1CBBpfcKIaK42K5cFy9X++t/P5MTp4gqoRIgSd+yyz8buCiQc54fIRSMpdq
21
- CgFiLGU8Eo4lYrQMgkXw2e1nj9vDsC698B331CnI/PKm26EaVjn3dh/anQKBgGxB
22
- FWRu+TmmyBQA6EIIOVogmqr6pDz2xienQhKLOR57huGX0acAkhHIdiKTnIUE9vDq
23
- h0Re9TgJw1LhjO1976RkqFDYBArnJJbQ9HcOqdMJ+kKlsjNA9QD773GyIitCueyH
24
- JRluuH91o8pfBS+FcEPmqvy2fA8EzeIpe4JjWpTzAoGAQHoipuzDbhb02XMqioc+
25
- UCsa6erj3kGrBUiFcvNutTG+oVq3qXOebOc9bCZcGHwsm0fgX5zk1XgY+IRfCWJY
26
- X14DQfLMBi/vObDXH9BaoVvc50EPSBW0V5k2aXavJ6XelbBXSI33bzGb+5wAltI/
27
- CFKf5ePEAi3jE69yYnTwbdc=
28
- -----END PRIVATE KEY-----
@@ -1 +0,0 @@
1
- 206DA63B52317CCDF0AA22CB1423BEEAE6C6747B
@@ -1,19 +0,0 @@
1
- -- Add up migration script here
2
- CREATE EXTENSION moddatetime;
3
-
4
- CREATE TABLE locks (
5
- id uuid DEFAULT gen_random_uuid(),
6
- lock_name VARCHAR NOT NULL,
7
- creator VARCHAR,
8
- ip INET,
9
- expires_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + (10 ||' minutes')::interval ,
10
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
11
- updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
12
- PRIMARY KEY (id),
13
- UNIQUE(lock_name)
14
- );
15
-
16
- CREATE TRIGGER locks_moddatetime
17
- BEFORE UPDATE ON locks
18
- FOR EACH ROW
19
- EXECUTE PROCEDURE moddatetime (updated_at);
File without changes
File without changes