From bc835f2f27e2b164d14785956292f642254fcea5 Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Thu, 23 Nov 2023 00:03:36 +0100 Subject: [PATCH] Updated to make sidecar working --- .docs/deployment-helm.md | 52 +- .docs/deployment-kubernetes-azure.md | 54 - .docs/deployment-kubernetes-minikube.md | 139 -- .docs/images/architecture.drawio | 20 +- .docs/images/exchange-binding.png | Bin 0 -> 4136 bytes .docs/images/queue-quorum.png | Bin 0 -> 15687 bytes .docs/system-services-broker.md | 18 +- .gitlab-ci.yml | 6 +- CHANGELOG.md | 2 +- Makefile | 8 +- dbrepo-data-db/sidecar/Dockerfile | 16 +- .../sidecar/clients/minio_client.py | 2 + dbrepo-metadata-db/setup-schema.sql | 1120 ++++++++--------- dbrepo-metadata-db/setup-schema_local.sql | 2 +- .../java/at/tuwien/mapper/QueryMapper.java | 2 +- dbrepo-ui/api/index.js | 6 +- dbrepo-ui/api/upload.service.js | 7 +- dbrepo-ui/dbrepo.config.json | 6 +- dbrepo-ui/nuxt.config.js | 3 +- .../database/_database_id/table/import.vue | 8 - docker-compose.prod.yml | 4 +- docker-compose.yml | 6 +- mkdocs.yml | 5 +- 23 files changed, 635 insertions(+), 851 deletions(-) delete mode 100644 .docs/deployment-kubernetes-azure.md delete mode 100644 .docs/deployment-kubernetes-minikube.md create mode 100644 .docs/images/exchange-binding.png create mode 100644 .docs/images/queue-quorum.png diff --git a/.docs/deployment-helm.md b/.docs/deployment-helm.md index c7164af43a..d7a144b8a5 100644 --- a/.docs/deployment-helm.md +++ b/.docs/deployment-helm.md @@ -4,60 +4,30 @@ author: Martin Weise ## TL;DR -To install DBRepo in your existing cluster, download the sample [`values.yaml`](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-deployment/-/raw/master/charts/dbrepo-core/values.yaml?inline=false) -for your deployment and update the variables, especially `hostname`. The chart depends on -installed [Keycloak Operator](https://www.keycloak.org/operator/installation) that can be installed following the -official guide. +To install DBRepo in your existing cluster, download the sample [`values.yaml`](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-deployment/-/raw/dev/charts/dbrepo-core/values.yaml?inline=false) +for your deployment and update the variables, especially `hostname`. ```shell helm upgrade --install dbrepo \ -n dbrepo \ "oci://dbrepo.azurecr.io/helm/dbrepo-core" \ --values ./values.yaml \ - --version "0.1.3" \ + --version "0.1.4-RC2" \ --create-namespace \ --cleanup-on-fail ``` ## Dependencies -The helm chart depends on four components: +Our chart depends on seven other charts which will be automatically resolved when installing our `dbrepo-core` chart: -1. [Ingress NGINX Controller](https://kubernetes.github.io/ingress-nginx/) for basic ingress. -2. [Cert-Manager Controller](https://cert-manager.io/) for TLS certificate management with Let's Encrypt. -3. [MariaDB Operator](https://github.com/mariadb-operator/mariadb-operator/) for creation of databases. -4. [Keycloak Operator](https://www.keycloak.org/operator/installation) for creation of the authentication service. - -## Configuration before the installation - -Define an admin user that the services can use to communicate with -the [Authentication Service](../system-services-authentication). You will need to manually create this user later after -the installation. - -## Configuration after the installation - -After installing, get the initial administrator password created by the [Keycloak operator](https://www.keycloak.org/operator/basic-deployment): - -```shell -kubectl -n dbrepo \ - get \ - secret \ - auth-service-initial-admin \ - -o jsonpath='{.data.password}' | base64 --decode -``` - -On success, the output should look like this: `1f5581a01d8e8f47f2dae08cc88f56fd` which is the initial password for the -user `admin`. This password should be considered as *temporary* and be changed immediately now! Login into -the [authentication service](../system-services-authentication) as `admin` and: - -1. Create a new user in the `master` realm. -2. Create credentials (non-temporary) for this user in the `master` realm. -3. Assign this user the role `admin`. -4. Delete the user `admin`. - -Then import the DBRepo realm by clicking the dropdown "master" > Create Realm and import -the [`dbrepo-realm.json`](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/raw/dev/dbrepo-authentication-service/dbrepo-realm.json) -by uploading the file *or* copying the contents and click "Create". +* Keycloak (Bitnami, v17.3.3) for [Authentication Service](../system-services-authentication) +* MariaDB Galera (Bitnami, v10.1.3) for [Data Database](../system-databases-data) & [Metadata Database](../system-databases-metadata) +* MinIO (Bitnami, v12.9.4) for [Storage Service](../system-services-storage) +* OpenSearch (OpenSearch Project, v2.16.0) for [Search Database](../system-databases-search) +* OpenSearch Dashboards (OpenSearch Project, v2.14.0) for [Search Dashboard](../system-other-search-dashboard) +* PostgreSQL HA (Bitnami, v12.1.7) for [Auth Database](../system-databases-auth) +* RabbitMQ (Bitnami, v12.5.1) for [Broker Service](../system-services-broker) ### Backup diff --git a/.docs/deployment-kubernetes-azure.md b/.docs/deployment-kubernetes-azure.md deleted file mode 100644 index 12f0a2e7de..0000000000 --- a/.docs/deployment-kubernetes-azure.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -author: Martin Weise ---- - -# Special Instructions for Azure Cloud - -You can use our pre-built Helm chart for deploying DBRepo in your Kubernetes Cluster -with Microsoft Azure as infrastructure provider. - -## Requirements - -### Hardware - -For this small cloud, test deployment any public cloud provider would suffice, we recommend a -small [Kubernetes Service](https://azure.microsoft.com/en-us/products/kubernetes-service) -with Kubernetes version *1.24.10* and node sizes *Standard_B4ms* - -- 4 vCPU cores -- 16GB RAM memory -- 200GB SSD storage - -This is roughly met by selecting the *Standard_B4ms* flavor and three worker nodes. - -## Deployment - -### Databases - -Since Azure offers a managed [Azure Database for MariaDB](https://azure.microsoft.com/en-us/products/mariadb), we -recommend to at least deploy the Metadata Database as high-available, managed database. - -!!! warning "End of Life software" - - Unfortunately, Azure does not (yet) support managed MariaDB 10.5, the latest version supported by Azure is 10.3 - which is End of Life (EOL) from [May 2023 onwards](https://mariadb.com/kb/en/changes-improvements-in-mariadb-10-3/). - Microsoft decided to still maintain MariaDB 10.3 - until [September 2025](https://learn.microsoft.com/en-us/azure/mariadb/concepts-supported-versions). - -### Fileshare - -For the shared volume *PersistentVolumeClaim* `dbrepo-shared-volume-claim`, select an appropriate *StorageClass* that -supports: - -1. Access mode `ReadWriteMany` -2. Hardlinks (TUSd creates lockfiles during upload) - -You will need to use a *StorageClass* of either `managed-*` or `azureblob-*` (after enabling the -proprietary [CSI driver for BLOB storage](https://learn.microsoft.com/en-us/azure/aks/azure-blob-csi?tabs=NFS#azure-blob-storage-csi-driver-features) -in your Kubernetes Cluster). - -We recommend to create -a [Container](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction#containers) for the -[Upload Service](../system-services-upload) to deposit files and mount the BLOB storage -via CSI drivers into the *Deployment*. It greatly increases the available interfaces (see below) for file uploads and -provides a highly-available filesystem for the many deployments that need to use the files. diff --git a/.docs/deployment-kubernetes-minikube.md b/.docs/deployment-kubernetes-minikube.md deleted file mode 100644 index 7c9376c575..0000000000 --- a/.docs/deployment-kubernetes-minikube.md +++ /dev/null @@ -1,139 +0,0 @@ ---- -author: Martin Weise ---- - -# Special Instructions for Minikube - -You can use our Helm chart for deploying DBRepo in your Kubernetes Cluster -using [minikube](https://minikube.sigs.k8s.io/docs/start/) as infrastructure provider which deploys a single-node Kubernetes cluster on your machine, -suitable for test-deployments. - -## Requirements - -### Virtual Machine - -For this small, local, test deployment any modern hardware would suffice, we recommend a dedicated virtual machine with -the following settings. Note that most of the vCPU and RAM resources will be needed for starting the infrastructure, -this is because of Docker. During idle times, the deployment will use significantly less resources. - -- 4 vCPU cores -- 16GB RAM memory -- 200GB SSD storage - -### Minikube - -First, install the minikube virtualization tool that provides a single-node Kubernetes environment, e.g. on a virtual -machine. We do not regularly check these instructions, they are provided on best-effort. Check -the [official documentation](https://minikube.sigs.k8s.io/docs/start/) for up-to-date information. - -For Debian: - -```shell -curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube_latest_amd64.deb -sudo dpkg -i minikube_latest_amd64.deb -``` - -Start the cluster and enable basic plugins: - -```shell -minikube start --driver='docker' -minikube kubectl -- get po -A -minikube addons enable ingress -``` - -### NGINX - -Deploy a NGINX reverse proxy on the virtual machine to reach your minikube cluster from the public Internet: - -```nginx title="/etc/nginx/conf.d/dbrepo.conf" -resolver 127.0.0.11 valid=30s; - -server { - listen 80; - server_name _; - - location / { - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_pass http://CLUSTER_IP; - } -} - -server { - listen 443 ssl; - server_name DOMAIN_NAME; - ssl_certificate /etc/nginx/certificate.crt; - ssl_certificate_key /etc/nginx/certificate.key; - - location / { - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_pass https://CLUSTER_IP; - } -} -``` - -Replace `CLUSTER_IP` with the result of: - - $ minikube ip - 192.168.49.2 - -Replace `DOMAIN_NAME` with the domain name. You will need also a valid TLS certificate with private key for TLS enabled -in the cluster. In our test deployment we obtained a certificate from Let's Encrypt. - -### Fileshare - -Since the Upload Service uses a shared filesystem with the [Analyst Service](../system-services-analyse), -[Metadata Service](../system-services-metadata) and -[Data Database](../system-databases-data), the dynamic provision of the *PersistentVolume* -by the *PersistentVolumeClaim* -of [`pvc.yaml`](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-deployment/-/blob/master/charts/dbrepo-core/templates/upload-service/pvc.yaml) -needs to happen statically. You can make use of the host's filesystem and mount it in each of those deployments. - -For example, mount the *hostPath* directly in -the [`deployment.yaml`](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-deployment/-/blob/master/charts/dbrepo-core/templates/analyse-service/deployment.yaml). - -```yaml title="deployment.yaml" -apiVersion: apps/v1 -kind: Deployment -metadata: - name: analyse-service - ... -spec: - template: - spec: - containers: - - name: analyse-service - volumeMounts: - - name: shared - hostPath: /path/of/host - mountPath: /mnt/shared - ... -``` - -## Deployment - -To install the DBRepo Helm Chart, download and edit -the [`values.yaml`](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-deployment/-/raw/master/charts/dbrepo-minikube/values.yaml?inline=false) -file. At minimum you need to change the values for: - -* `hostname`, set to your domain, e.g. `subdomain.example.com` -* `authAdminApiUrl`, similarly but with https and the api to the keycloak server, e.g. `https://subdomain.example.com/api/auth` - -It is advised to also change the usernames and passwords for all credentials. Next, install the chart using your edited -`values.yaml` file: - -!!! info "Documentation of values.yaml" - - We documented all values in the `values.yaml` file [here](http://127.0.0.1:8000/deployment-helm/#chart-values) with - default values and description for each value. - -```shell -helm upgrade --install dbrepo \ - -n dbrepo \ - "oci://dbrepo.azurecr.io/helm/dbrepo-core" \ - --values ./values.yaml \ - --version "0.1.3" \ - --create-namespace \ - --cleanup-on-fail -``` diff --git a/.docs/images/architecture.drawio b/.docs/images/architecture.drawio index ff24e74e84..025aef75dc 100644 --- a/.docs/images/architecture.drawio +++ b/.docs/images/architecture.drawio @@ -1,4 +1,4 @@ -<mxfile host="Electron" modified="2023-11-17T11:53:24.549Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.1.2 Chrome/106.0.5249.199 Electron/21.4.3 Safari/537.36" etag="BtfeOmnXFlr2YLKq2VZ0" version="21.1.2" type="device" pages="7"> +<mxfile host="Electron" modified="2023-11-22T22:51:30.674Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.1.2 Chrome/106.0.5249.199 Electron/21.4.3 Safari/537.36" etag="rfDZiCzC6Lc0py7Ucjta" version="21.1.2" type="device" pages="7"> <diagram id="mvBsv1rP8O80Qe3yGnn_" name="docker-compose"> <mxGraphModel dx="1434" dy="822" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0"> <root> @@ -798,38 +798,38 @@ </mxGraphModel> </diagram> <diagram id="n3Gsc6DDUkQ8nNTTz0wk" name="data-db"> - <mxGraphModel dx="819" dy="-180" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1019" pageHeight="650" math="0" shadow="0"> + <mxGraphModel dx="1434" dy="172" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1019" pageHeight="650" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> - <mxCell id="S8wz9ZtwZs3Sd4maCRdY-9" value="shared filesystem<br>/tmp" style="rounded=1;whiteSpace=wrap;html=1;arcSize=3;verticalAlign=bottom;fontStyle=2" vertex="1" parent="1"> + <mxCell id="S8wz9ZtwZs3Sd4maCRdY-9" value="shared filesystem<br>/tmp" style="rounded=1;whiteSpace=wrap;html=1;arcSize=3;verticalAlign=bottom;fontStyle=2" parent="1" vertex="1"> <mxGeometry x="425" y="840" width="248" height="130" as="geometry" /> </mxCell> - <mxCell id="S8wz9ZtwZs3Sd4maCRdY-11" value="jdbc" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;endArrow=none;endFill=0;startArrow=classic;startFill=1;" edge="1" parent="1" source="S8wz9ZtwZs3Sd4maCRdY-1"> + <mxCell id="S8wz9ZtwZs3Sd4maCRdY-11" value="jdbc" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;endArrow=none;endFill=0;startArrow=classic;startFill=1;" parent="1" source="S8wz9ZtwZs3Sd4maCRdY-1" edge="1"> <mxGeometry x="0.3769" relative="1" as="geometry"> <mxPoint x="472.71428571428555" y="810" as="targetPoint" /> <mxPoint as="offset" /> </mxGeometry> </mxCell> - <mxCell id="S8wz9ZtwZs3Sd4maCRdY-1" value="" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=8.600000000000023;fillColor=#dae8fc;strokeColor=#000000;" vertex="1" parent="1"> + <mxCell id="S8wz9ZtwZs3Sd4maCRdY-1" value="" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=8.600000000000023;fillColor=#dae8fc;strokeColor=#000000;" parent="1" vertex="1"> <mxGeometry x="447.5" y="857" width="50" height="64" as="geometry" /> </mxCell> - <mxCell id="S8wz9ZtwZs3Sd4maCRdY-2" value="data-db" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;spacing=-1;" vertex="1" parent="1"> + <mxCell id="S8wz9ZtwZs3Sd4maCRdY-2" value="data-db" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;spacing=-1;" parent="1" vertex="1"> <mxGeometry x="431.5" y="919" width="85" height="20" as="geometry" /> </mxCell> - <mxCell id="S8wz9ZtwZs3Sd4maCRdY-12" value="http" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;startArrow=classic;startFill=1;endArrow=none;endFill=0;" edge="1" parent="1" source="S8wz9ZtwZs3Sd4maCRdY-7"> + <mxCell id="S8wz9ZtwZs3Sd4maCRdY-12" value="http" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;startArrow=classic;startFill=1;endArrow=none;endFill=0;" parent="1" source="S8wz9ZtwZs3Sd4maCRdY-7" edge="1"> <mxGeometry x="0.4743" relative="1" as="geometry"> <mxPoint x="585.0952380952381" y="810" as="targetPoint" /> <mxPoint as="offset" /> </mxGeometry> </mxCell> - <mxCell id="m0IQrUpga-DAo2afT193-3" value="S3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;startArrow=classic;startFill=1;" edge="1" parent="1" source="S8wz9ZtwZs3Sd4maCRdY-7" target="m0IQrUpga-DAo2afT193-1"> + <mxCell id="m0IQrUpga-DAo2afT193-3" value="S3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;startArrow=classic;startFill=1;" parent="1" source="S8wz9ZtwZs3Sd4maCRdY-7" target="m0IQrUpga-DAo2afT193-1" edge="1"> <mxGeometry relative="1" as="geometry" /> </mxCell> - <mxCell id="S8wz9ZtwZs3Sd4maCRdY-7" value="Data DB Sidecar" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1"> + <mxCell id="S8wz9ZtwZs3Sd4maCRdY-7" value="Data DB Sidecar" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"> <mxGeometry x="520" y="869" width="130" height="40" as="geometry" /> </mxCell> - <mxCell id="m0IQrUpga-DAo2afT193-1" value="Storage Service<br>(minIO)" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1"> + <mxCell id="m0IQrUpga-DAo2afT193-1" value="Storage Service<br>(minIO)" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1"> <mxGeometry x="720" y="869" width="130" height="40" as="geometry" /> </mxCell> </root> diff --git a/.docs/images/exchange-binding.png b/.docs/images/exchange-binding.png new file mode 100644 index 0000000000000000000000000000000000000000..92517861c5b915f30d524dc5f785b30c2ca9b77a GIT binary patch literal 4136 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 literal 0 HcmV?d00001 diff --git a/.docs/images/queue-quorum.png b/.docs/images/queue-quorum.png new file mode 100644 index 0000000000000000000000000000000000000000..42897a0691b9c6bd814a76e4cef926c5bdbd8939 GIT binary patch literal 15687 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 Bg0uht literal 0 HcmV?d00001 diff --git a/.docs/system-services-broker.md b/.docs/system-services-broker.md index ff259c4615..f7919f8bd2 100644 --- a/.docs/system-services-broker.md +++ b/.docs/system-services-broker.md @@ -21,7 +21,7 @@ It holds exchanges and topics responsible for holding AMQP messages for later co use [RabbitMQ](https://www.rabbitmq.com/) in the implementation. By default, the endpoint listens to the insecure port `5672` for incoming AMQP tuples and insecure port `15672` for the management UI. -The default configuration creates a user with administrative privileges: +The default configuration creates a user with administrative privileges on the default virtual host `dbrepo`: * Username: `fda` * Password: `fda` @@ -35,6 +35,22 @@ The Broker Service allows two ways of authentication: For detailed examples how to authenticate with the Broker Service see the [usage](/usage-broker) page. +The architecture of the Broker Service is very simple. There is only one durable, topic exchange `dbrepo` and one quorum +queue `dbrepo`, connected with a binding of `dbrepo.#` which routes all tuples with routing key prefix `dbrepo.` (mind +the dot!) to this queue. + +<figure markdown> +  + <figcaption>Replicated quorum queue dbrepo in a cluster with three nodes</figcaption> +</figure> + +The consumer takes care of writing it to the correct table in the [Data Service](../system-services-data). + +<figure markdown> +  + <figcaption>Architecture Broker Service</figcaption> +</figure> + ## Limitations * No support for MQTT in the [Metadata Service](../system-services-metadata) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0f4a06f328..f0feb7e233 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -349,9 +349,9 @@ scan-data-db: - master allow_failure: true script: - - trivy image --insecure --exit-code 0 --format template --template "@.trivy/gitlab.tpl" -o ./.trivy/trivy-data-db-report.json docker.io/bitnami/mariadb:10.5 - - trivy image --insecure --exit-code 0 docker.io/bitnami/mariadb:10.5 - - trivy image --insecure --exit-code 1 --severity CRITICAL docker.io/bitnami/mariadb:10.5 + - trivy image --insecure --exit-code 0 --format template --template "@.trivy/gitlab.tpl" -o ./.trivy/trivy-data-db-report.json docker.io/bitnami/mariadb:11.1.3 + - trivy image --insecure --exit-code 0 docker.io/bitnami/mariadb:11.1.3 + - trivy image --insecure --exit-code 1 --severity CRITICAL docker.io/bitnami/mariadb:11.1.3 cache: paths: - .trivycache/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 81ad2a31a9..2ed25b07c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,7 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Metadata database to use a system-versioned MariaDB 10.5 database +- Metadata database to use a system-versioned MariaDB 11.1.3 database - Query Store to use trigger for query result count and query hash as well as result hash calculation - Query service to allow XML/CSV export for PIDs - Query service to support subsets of views diff --git a/Makefile b/Makefile index a9c6b7d970..5bc096951b 100644 --- a/Makefile +++ b/Makefile @@ -211,10 +211,10 @@ scan-search-dashboard: trivy image --insecure --exit-code 1 --severity CRITICAL "opensearchproject/opensearch-dashboards:2.10.0" scan-data-db: - docker pull "bitnami/mariadb:10.5" - trivy image --insecure --exit-code 0 --format template --template "@.trivy/gitlab.tpl" -o ./.trivy/trivy-data-db-report.json "bitnami/mariadb:10.5" - trivy image --insecure --exit-code 0 "bitnami/mariadb:10.5" - trivy image --insecure --exit-code 1 --severity CRITICAL "bitnami/mariadb:10.5" + docker pull "bitnami/mariadb:11.1.3" + trivy image --insecure --exit-code 0 --format template --template "@.trivy/gitlab.tpl" -o ./.trivy/trivy-data-db-report.json "bitnami/mariadb:11.1.3" + trivy image --insecure --exit-code 0 "bitnami/mariadb:11.1.3" + trivy image --insecure --exit-code 1 --severity CRITICAL "bitnami/mariadb:11.1.3" scan-ui: trivy image --insecure --exit-code 0 --format template --template "@.trivy/gitlab.tpl" -o ./.trivy/trivy-ui-report.json dbrepo-ui:latest diff --git a/dbrepo-data-db/sidecar/Dockerfile b/dbrepo-data-db/sidecar/Dockerfile index 0d307ae675..755ee168eb 100644 --- a/dbrepo-data-db/sidecar/Dockerfile +++ b/dbrepo-data-db/sidecar/Dockerfile @@ -2,7 +2,7 @@ FROM python:3.10-alpine RUN apk add bash curl jq -WORKDIR /app +ENV PYTHONFAULTHANDLER=1 COPY Pipfile Pipfile.lock ./ @@ -10,18 +10,18 @@ RUN pip install pipenv && \ pipenv install gunicorn && \ pipenv install --system --deploy -USER 1000 +USER 1001 + +WORKDIR /app -COPY ./clients ./clients -COPY ./ds-yml ./ds-yml -COPY ./app.py ./app.py +COPY --chown=1001 ./clients ./clients +COPY --chown=1001 ./ds-yml ./ds-yml +COPY --chown=1001 ./app.py ./app.py ENV S3_STORAGE_ENDPOINT="http://storage-service:9000" ENV S3_ACCESS_KEY_ID="minioadmin" ENV S3_SECRET_ACCESS_KEY="minioadmin" -RUN ls -la ./clients - EXPOSE 3305 -ENTRYPOINT [ "gunicorn", "-w", "4", "-b", ":3305", "app:app" ] +ENTRYPOINT [ "gunicorn", "--log-level", "DEBUG", "--workers", "4", "--bind", ":3305", "app:app" ] diff --git a/dbrepo-data-db/sidecar/clients/minio_client.py b/dbrepo-data-db/sidecar/clients/minio_client.py index a3f27dbb07..9f38a83b49 100644 --- a/dbrepo-data-db/sidecar/clients/minio_client.py +++ b/dbrepo-data-db/sidecar/clients/minio_client.py @@ -55,6 +55,7 @@ class MinioClient: def file_exists(self, bucket, filename): try: self.client.head_object(Bucket=bucket, Key=filename) + logging.debug(f"file with name {filename} exists in bucket with name {bucket}") except ClientError as e: if e.response["Error"]["Code"] == "404": logging.error("Failed to find key %s in bucket %s", filename, bucket) @@ -66,6 +67,7 @@ class MinioClient: def bucket_exists_or_exit(self, bucket): try: self.client.head_bucket(Bucket=bucket) + logging.debug(f"bucket {bucket} exists.") except ClientError as e: if e.response["Error"]["Code"] == "404": logging.error("Failed to find bucket %s", bucket) diff --git a/dbrepo-metadata-db/setup-schema.sql b/dbrepo-metadata-db/setup-schema.sql index c42a754e86..c418a3f422 100644 --- a/dbrepo-metadata-db/setup-schema.sql +++ b/dbrepo-metadata-db/setup-schema.sql @@ -1,560 +1,560 @@ -BEGIN; - -CREATE TABLE IF NOT EXISTS `mdb_users` -( - id character varying(36) NOT NULL, - username character varying(255) NOT NULL, - firstname character varying(255), - lastname character varying(255), - email character varying(255) NOT NULL, - orcid character varying(255), - affiliation character varying(255), - mariadb_password character varying(255) NOT NULL, - theme_dark boolean, - PRIMARY KEY (id), - UNIQUE (username), - UNIQUE (email) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_images` -( - id bigint NOT NULL AUTO_INCREMENT, - name character varying(255) NOT NULL, - version character varying(255) NOT NULL, - default_port integer NOT NULL, - dialect character varying(255) NOT NULL, - driver_class character varying(255) NOT NULL, - jdbc_method character varying(255) NOT NULL, - created timestamp NOT NULL DEFAULT NOW(), - last_modified timestamp, - PRIMARY KEY (id), - UNIQUE (name, version) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_images_date` -( - id bigint NOT NULL AUTO_INCREMENT, - iid bigint NOT NULL, - database_format character varying(255) NOT NULL, - unix_format character varying(255) NOT NULL, - example character varying(255) NOT NULL, - has_time boolean NOT NULL, - created_at timestamp NOT NULL DEFAULT NOW(), - PRIMARY KEY (id), - FOREIGN KEY (iid) REFERENCES mdb_images (id), - UNIQUE (database_format, unix_format, example) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_containers` -( - id bigint NOT NULL AUTO_INCREMENT, - internal_name character varying(255) NOT NULL, - name character varying(255) NOT NULL, - host character varying(255) NOT NULL, - port integer NOT NULL default 3306, - ui_host character varying(255) NOT NULL default host, - ui_port integer NOT NULL default port, - ui_additional_flags text, - sidecar_host character varying(255) NOT NULL, - sidecar_port integer NOT NULL default 3305, - image_id bigint NOT NULL, - created timestamp NOT NULL DEFAULT NOW(), - last_modified timestamp, - privileged_username character varying(255) NOT NULL, - privileged_password character varying(255) NOT NULL, - PRIMARY KEY (id), - FOREIGN KEY (image_id) REFERENCES mdb_images (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_data` -( - ID bigint NOT NULL AUTO_INCREMENT, - PROVENANCE text, - FileEncoding text, - FileType character varying(100), - Version text, - Seperator text, - PRIMARY KEY (ID) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_licenses` -( - identifier character varying(255) NOT NULL, - uri text NOT NULL, - PRIMARY KEY (identifier), - UNIQUE (uri(200)) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_databases` -( - id bigint NOT NULL AUTO_INCREMENT, - cid bigint NOT NULL, - name character varying(255) NOT NULL, - internal_name character varying(255) NOT NULL, - exchange_name character varying(255) NOT NULL, - description text, - engine character varying(20), - is_public boolean NOT NULL DEFAULT TRUE, - created_by character varying(36), - owned_by character varying(36), - contact_person character varying(36), - created timestamp NOT NULL DEFAULT NOW(), - last_modified timestamp, - PRIMARY KEY (id), - FOREIGN KEY (cid) REFERENCES mdb_containers (id) /* currently we only support one-to-one */, - FOREIGN KEY (created_by) REFERENCES mdb_users (id), - FOREIGN KEY (owned_by) REFERENCES mdb_users (id), - FOREIGN KEY (contact_person) REFERENCES mdb_users (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_databases_subjects` -( - dbid BIGINT NOT NULL, - subjects character varying(255) NOT NULL, - PRIMARY KEY (dbid, subjects) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_tables` -( - ID bigint NOT NULL AUTO_INCREMENT, - tDBID bigint NOT NULL, - internal_name character varying(255) NOT NULL, - queue_name character varying(255) NOT NULL, - routing_key character varying(255) NOT NULL, - tName VARCHAR(50), - tDescription TEXT, - NumCols INTEGER, - NumRows INTEGER, - `separator` CHAR(1), - quote CHAR(1), - element_null VARCHAR(50), - skip_lines BIGINT, - element_true VARCHAR(50), - element_false VARCHAR(50), - Version TEXT, - created timestamp NOT NULL DEFAULT NOW(), - versioned boolean not null default true, - created_by character varying(36) NOT NULL, - owned_by character varying(36) NOT NULL, - last_modified timestamp, - PRIMARY KEY (ID), - FOREIGN KEY (tDBID) REFERENCES mdb_databases (id), - FOREIGN KEY (created_by) REFERENCES mdb_users (id), - FOREIGN KEY (owned_by) REFERENCES mdb_users (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_columns` -( - ID bigint NOT NULL AUTO_INCREMENT, - tID bigint NOT NULL, - dfID bigint, - cName VARCHAR(100), - internal_name VARCHAR(100) NOT NULL, - alias VARCHAR(100), - Datatype ENUM ('CHAR','VARCHAR','BINARY','VARBINARY','TINYBLOB','TINYTEXT','TEXT','BLOB','MEDIUMTEXT','MEDIUMBLOB','LONGTEXT','LONGBLOB','ENUM','SET','BIT','TINYINT','BOOL','SMALLINT','MEDIUMINT','INT','BIGINT','FLOAT','DOUBLE','DECIMAL','DATE','DATETIME','TIMESTAMP','TIME','YEAR'), - length INT NULL, - ordinal_position INTEGER NOT NULL, - is_primary_key BOOLEAN NOT NULL, - index_length INT NULL, - size INT, - d INT, - auto_generated BOOLEAN DEFAULT false, - is_null_allowed BOOLEAN NOT NULL DEFAULT true, - created timestamp NOT NULL DEFAULT NOW(), - last_modified timestamp, - FOREIGN KEY (tID) REFERENCES mdb_tables (ID), - PRIMARY KEY (ID) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_columns_enums` -( - id bigint NOT NULL AUTO_INCREMENT, - column_id bigint NOT NULL, - value CHARACTER VARYING(255) NOT NULL, - FOREIGN KEY (column_id) REFERENCES mdb_columns (ID), - PRIMARY KEY (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_columns_sets` -( - id bigint NOT NULL AUTO_INCREMENT, - column_id bigint NOT NULL, - value CHARACTER VARYING(255) NOT NULL, - FOREIGN KEY (column_id) REFERENCES mdb_columns (ID), - PRIMARY KEY (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_columns_nom` -( - tID bigint, - cID bigint, - maxlength INTEGER, - last_modified timestamp, - created timestamp NOT NULL DEFAULT NOW(), - FOREIGN KEY (tID, cID) REFERENCES mdb_columns (tID, ID), - PRIMARY KEY (tID, cID) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_columns_num` -( - tID bigint, - cID bigint, - SIunit TEXT, - MaxVal NUMERIC, - MinVal NUMERIC, - Mean NUMERIC, - Median NUMERIC, - Sd Numeric, --- Histogram INTEGER[], - last_modified timestamp, - created timestamp NOT NULL DEFAULT NOW(), - FOREIGN KEY (tID, cID) REFERENCES mdb_columns (tID, ID), - PRIMARY KEY (tID, cID) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_columns_cat` -( - tID bigint, - cID bigint, - num_cat INTEGER, --- cat_array TEXT[], - last_modified timestamp, - created timestamp NOT NULL DEFAULT NOW(), - FOREIGN KEY (tID, cID) REFERENCES mdb_columns (tID, ID), - PRIMARY KEY (tID, cID) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_constraints_foreign_key` -( - fkid BIGINT NOT NULL AUTO_INCREMENT, - tid BIGINT NOT NULL, - rtid BIGINT NOT NULL, - on_update VARCHAR(50) NULL, - on_delete VARCHAR(50) NULL, - position INT NULL, - PRIMARY KEY (fkid), - FOREIGN KEY (tid) REFERENCES mdb_tables (id), - FOREIGN KEY (rtid) REFERENCES mdb_tables (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_constraints_foreign_key_reference` -( - id BIGINT NOT NULL AUTO_INCREMENT, - fkid BIGINT NOT NULL, - cid BIGINT NOT NULL, - rcid BIGINT NOT NULL, - PRIMARY KEY (id), - FOREIGN KEY (fkid) REFERENCES mdb_constraints_foreign_key (fkid) ON UPDATE CASCADE, - FOREIGN KEY (cid) REFERENCES mdb_columns (id), - FOREIGN KEY (rcid) REFERENCES mdb_columns (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_constraints_unique` -( - uid BIGINT NOT NULL AUTO_INCREMENT, - tid BIGINT NOT NULL, - position INT NULL, - PRIMARY KEY (uid), - FOREIGN KEY (tid) REFERENCES mdb_tables (id) -); - -CREATE TABLE IF NOT EXISTS `mdb_constraints_unique_columns` -( - id BIGINT NOT NULL AUTO_INCREMENT, - uid BIGINT NOT NULL, - cid BIGINT NOT NULL, - PRIMARY KEY (id), - FOREIGN KEY (uid) REFERENCES mdb_constraints_unique (uid), - FOREIGN KEY (cid) REFERENCES mdb_columns (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_constraints_checks` -( - id BIGINT NOT NULL AUTO_INCREMENT, - tid BIGINT NOT NULL, - checks VARCHAR(255) NOT NULL, - PRIMARY KEY (id), - FOREIGN KEY (tid) REFERENCES mdb_tables (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_concepts` -( - id bigint NOT NULL AUTO_INCREMENT, - uri text not null, - name VARCHAR(255) null, - description TEXT null, - created timestamp NOT NULL DEFAULT NOW(), - PRIMARY KEY (id), - UNIQUE (uri(200)) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_units` -( - id bigint NOT NULL AUTO_INCREMENT, - uri text not null, - name VARCHAR(255) null, - description TEXT null, - created timestamp NOT NULL DEFAULT NOW(), - PRIMARY KEY (id), - UNIQUE (uri(200)) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_columns_concepts` -( - id bigint NOT NULL, - cID bigint NOT NULL, - created timestamp NOT NULL DEFAULT NOW(), - PRIMARY KEY (id), - FOREIGN KEY (cID) REFERENCES mdb_columns (ID) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_columns_units` -( - id bigint NOT NULL, - cID bigint NOT NULL, - created timestamp NOT NULL DEFAULT NOW(), - PRIMARY KEY (id), - FOREIGN KEY (cID) REFERENCES mdb_columns (ID) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_view` -( - id bigint NOT NULL AUTO_INCREMENT, - vdbid bigint NOT NULL, - vName VARCHAR(255) NOT NULL, - internal_name VARCHAR(255) NOT NULL, - Query TEXT NOT NULL, - query_hash VARCHAR(255) NOT NULL, - Public BOOLEAN NOT NULL, - NumCols INTEGER, - NumRows INTEGER, - InitialView BOOLEAN NOT NULL, - created timestamp NOT NULL DEFAULT NOW(), - last_modified timestamp, - created_by character varying(36) NOT NULL, - PRIMARY KEY (id), - FOREIGN KEY (vdbid) REFERENCES mdb_databases (id), - FOREIGN KEY (created_by) REFERENCES mdb_users (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_banner_messages` -( - id bigint NOT NULL AUTO_INCREMENT, - type ENUM ('ERROR', 'WARNING', 'INFO') NOT NULL default 'INFO', - message TEXT NOT NULL, - link TEXT NULL, - link_text VARCHAR(255) NULL, - display_start timestamp NULL, - display_end timestamp NULL, - PRIMARY KEY (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_ontologies` -( - id bigint NOT NULL AUTO_INCREMENT, - prefix VARCHAR(8) NOT NULL, - uri TEXT NOT NULL, - uri_pattern TEXT, - sparql_endpoint TEXT NULL, - last_modified timestamp, - created timestamp NOT NULL DEFAULT NOW(), - UNIQUE (prefix), - UNIQUE (uri(200)), - PRIMARY KEY (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_view_columns` -( - id BIGINT NOT NULL AUTO_INCREMENT, - cid BIGINT NOT NULL, - vid BIGINT NOT NULL, - position INTEGER NULL, - PRIMARY KEY (id), - FOREIGN KEY (vid) REFERENCES mdb_view (id), - FOREIGN KEY (cid) REFERENCES mdb_columns (ID) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_identifiers` -( - id bigint NOT NULL AUTO_INCREMENT, - dbid bigint, - qid bigint, - vid bigint, - publisher VARCHAR(255) NOT NULL, - language VARCHAR(2), - visibility ENUM ('SELF', 'EVERYONE') NOT NULL default 'EVERYONE', - publication_year INTEGER NOT NULL, - publication_month INTEGER, - publication_day INTEGER, - identifier_type ENUM ('DATABASE', 'SUBSET', 'VIEW') NOT NULL, - query TEXT, - query_normalized TEXT, - query_hash VARCHAR(255), - execution timestamp, - result_hash VARCHAR(255), - result_number bigint, - doi VARCHAR(255), - created timestamp NOT NULL DEFAULT NOW(), - created_by character varying(36) NOT NULL, - last_modified timestamp, - PRIMARY KEY (id), /* must be a single id from persistent identifier concept */ - FOREIGN KEY (dbid) REFERENCES mdb_databases (id), - UNIQUE (dbid, qid), - FOREIGN KEY (created_by) REFERENCES mdb_users (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_identifier_licenses` -( - pid bigint NOT NULL, - license_id VARCHAR(255) NOT NULL, - PRIMARY KEY (pid, license_id), - FOREIGN KEY (pid) REFERENCES mdb_identifiers (id), - FOREIGN KEY (license_id) REFERENCES mdb_licenses (identifier) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_identifier_titles` -( - id bigint NOT NULL AUTO_INCREMENT, - pid bigint NOT NULL, - title text NOT NULL, - title_type ENUM ('ALTERNATIVE_TITLE', 'SUBTITLE', 'TRANSLATED_TITLE', 'OTHER'), - language VARCHAR(2), - PRIMARY KEY (id), - FOREIGN KEY (pid) REFERENCES mdb_identifiers (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_identifier_funders` -( - id bigint NOT NULL AUTO_INCREMENT, - pid bigint NOT NULL, - funder_name VARCHAR(255) NOT NULL, - funder_identifier TEXT, - funder_identifier_type ENUM ('CROSSREF_FUNDER_ID', 'GRID', 'ISNI', 'ROR', 'OTHER'), - scheme_uri text, - award_number VARCHAR(255), - award_title text, - language VARCHAR(255), - PRIMARY KEY (id), - FOREIGN KEY (pid) REFERENCES mdb_identifiers (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_identifier_descriptions` -( - id bigint NOT NULL AUTO_INCREMENT, - pid bigint NOT NULL, - description text NOT NULL, - description_type ENUM ('ABSTRACT', 'METHODS', 'SERIES_INFORMATION', 'TABLE_OF_CONTENTS', 'TECHNICAL_INFO', 'OTHER'), - language VARCHAR(2), - PRIMARY KEY (id), - FOREIGN KEY (pid) REFERENCES mdb_identifiers (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_related_identifiers` -( - id bigint NOT NULL AUTO_INCREMENT, - pid bigint NOT NULL, - value varchar(255) NOT NULL, - type varchar(255), - relation varchar(255), - PRIMARY KEY (id), /* must be a single id from persistent identifier concept */ - FOREIGN KEY (pid) REFERENCES mdb_identifiers (id), - UNIQUE (pid, value) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_identifier_creators` -( - id bigint NOT NULL AUTO_INCREMENT, - pid bigint NOT NULL, - given_names text, - family_name text, - creator_name VARCHAR(255) NOT NULL, - name_type ENUM ('PERSONAL', 'ORGANIZATIONAL') default 'PERSONAL', - name_identifier text, - name_identifier_scheme ENUM ('ROR', 'GRID', 'ISNI', 'ORCID'), - name_identifier_scheme_uri text, - affiliation VARCHAR(255), - affiliation_identifier text, - affiliation_identifier_scheme ENUM ('ROR', 'GRID', 'ISNI'), - affiliation_identifier_scheme_uri text, - PRIMARY KEY (id), - FOREIGN KEY (pid) REFERENCES mdb_identifiers (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_feed` -( - fDBID bigint, - fID bigint, - fUserId character varying(36) not null, - fDataID bigint REFERENCES mdb_data (ID), - created timestamp NOT NULL DEFAULT NOW(), - PRIMARY KEY (fDBID, fID, fUserId, fDataID), - FOREIGN KEY (fDBID, fID) REFERENCES mdb_tables (tDBID, ID), - FOREIGN KEY (fUserId) REFERENCES mdb_users (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_update` -( - uUserID character varying(255) NOT NULL, - uDBID bigint NOT NULL, - created timestamp NOT NULL DEFAULT NOW(), - PRIMARY KEY (uUserID, uDBID), - FOREIGN KEY (uDBID) REFERENCES mdb_databases (id) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_access` -( - aUserID character varying(255) NOT NULL, - aDBID bigint REFERENCES mdb_databases (id), - attime TIMESTAMP, - download BOOLEAN, - created timestamp NOT NULL DEFAULT NOW(), - PRIMARY KEY (aUserID, aDBID) -) WITH SYSTEM VERSIONING; - -CREATE TABLE IF NOT EXISTS `mdb_have_access` -( - user_id character varying(36) NOT NULL, - database_id bigint REFERENCES mdb_databases (id), - access_type ENUM ('READ', 'WRITE_OWN', 'WRITE_ALL') NOT NULL, - created timestamp NOT NULL DEFAULT NOW(), - PRIMARY KEY (user_id, database_id), - FOREIGN KEY (user_id) REFERENCES mdb_users (id) -) WITH SYSTEM VERSIONING; - -COMMIT; -BEGIN; - -INSERT INTO `mdb_licenses` (identifier, uri) -VALUES ('MIT', 'https://opensource.org/licenses/MIT'), - ('GPL-3.0-only', 'https://www.gnu.org/licenses/gpl-3.0-standalone.html'), - ('BSD-3-Clause', 'https://opensource.org/licenses/BSD-3-Clause'), - ('BSD-4-Clause', 'http://directory.fsf.org/wiki/License:BSD_4Clause'), - ('Apache-2.0', 'https://opensource.org/licenses/Apache-2.0'), - ('CC0-1.0', 'https://creativecommons.org/publicdomain/zero/1.0/legalcode'), - ('CC-BY-4.0', 'https://creativecommons.org/licenses/by/4.0/legalcode'); - -INSERT INTO `mdb_images` (name, version, default_port, dialect, driver_class, jdbc_method) -VALUES ('mariadb', '10.5', 3306, 'org.hibernate.dialect.MariaDBDialect', 'org.mariadb.jdbc.Driver', 'mariadb'); - -INSERT INTO `mdb_images_date` (iid, database_format, unix_format, example, has_time) -VALUES (1, '%Y-%c-%d %H:%i:%S.%f', 'yyyy-MM-dd HH:mm:ss.SSSSSS', '2022-01-30 13:44:25.499', true), - (1, '%Y-%c-%d %H:%i:%S', 'yyyy-MM-dd HH:mm:ss', '2022-01-30 13:44:25', true), - (1, '%Y-%c-%d', 'yyyy-MM-dd', '2022-01-30', false), - (1, '%H:%i:%S', 'HH:mm:ss', '13:44:25', true); - -INSERT INTO `mdb_ontologies` (prefix, uri, uri_pattern, sparql_endpoint) -VALUES ('om', 'http://www.ontology-of-units-of-measure.org/resource/om-2/', - 'http://www.ontology-of-units-of-measure.org/resource/om-2/.*', null), - ('wd', 'http://www.wikidata.org/', 'http://www.wikidata.org/entity/.*', 'https://query.wikidata.org/sparql'), - ('mo', 'http://purl.org/ontology/mo/', 'http://purl.org/ontology/mo/.*', null), - ('dc', 'http://purl.org/dc/elements/1.1/', null, null), - ('xsd', 'http://www.w3.org/2001/XMLSchema#', null, null), - ('tl', 'http://purl.org/NET/c4dm/timeline.owl#', null, null), - ('foaf', 'http://xmlns.com/foaf/0.1/', null, null), - ('schema', 'http://schema.org/', null, null), - ('rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', null, null), - ('rdfs', 'http://www.w3.org/2000/01/rdf-schema#', null, null), - ('owl', 'http://www.w3.org/2002/07/owl#', null, null), - ('prov', 'http://www.w3.org/ns/prov#', null, null), - ('db', 'http://dbpedia.org', 'http://dbpedia.org/ontology/.*', 'http://dbpedia.org/sparql'); -COMMIT; + BEGIN; + + CREATE TABLE IF NOT EXISTS `mdb_users` + ( + id character varying(36) NOT NULL, + username character varying(255) NOT NULL, + firstname character varying(255), + lastname character varying(255), + email character varying(255) NOT NULL, + orcid character varying(255), + affiliation character varying(255), + mariadb_password character varying(255) NOT NULL, + theme_dark boolean, + PRIMARY KEY (id), + UNIQUE (username), + UNIQUE (email) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_images` + ( + id bigint NOT NULL AUTO_INCREMENT, + name character varying(255) NOT NULL, + version character varying(255) NOT NULL, + default_port integer NOT NULL, + dialect character varying(255) NOT NULL, + driver_class character varying(255) NOT NULL, + jdbc_method character varying(255) NOT NULL, + created timestamp NOT NULL DEFAULT NOW(), + last_modified timestamp, + PRIMARY KEY (id), + UNIQUE (name, version) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_images_date` + ( + id bigint NOT NULL AUTO_INCREMENT, + iid bigint NOT NULL, + database_format character varying(255) NOT NULL, + unix_format character varying(255) NOT NULL, + example character varying(255) NOT NULL, + has_time boolean NOT NULL, + created_at timestamp NOT NULL DEFAULT NOW(), + PRIMARY KEY (id), + FOREIGN KEY (iid) REFERENCES mdb_images (id), + UNIQUE (database_format, unix_format, example) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_containers` + ( + id bigint NOT NULL AUTO_INCREMENT, + internal_name character varying(255) NOT NULL, + name character varying(255) NOT NULL, + host character varying(255) NOT NULL, + port integer NOT NULL default 3306, + ui_host character varying(255) NOT NULL default host, + ui_port integer NOT NULL default port, + ui_additional_flags text, + sidecar_host character varying(255) NOT NULL, + sidecar_port integer NOT NULL default 3305, + image_id bigint NOT NULL, + created timestamp NOT NULL DEFAULT NOW(), + last_modified timestamp, + privileged_username character varying(255) NOT NULL, + privileged_password character varying(255) NOT NULL, + PRIMARY KEY (id), + FOREIGN KEY (image_id) REFERENCES mdb_images (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_data` + ( + ID bigint NOT NULL AUTO_INCREMENT, + PROVENANCE text, + FileEncoding text, + FileType character varying(100), + Version text, + Seperator text, + PRIMARY KEY (ID) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_licenses` + ( + identifier character varying(255) NOT NULL, + uri text NOT NULL, + PRIMARY KEY (identifier), + UNIQUE (uri(200)) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_databases` + ( + id bigint NOT NULL AUTO_INCREMENT, + cid bigint NOT NULL, + name character varying(255) NOT NULL, + internal_name character varying(255) NOT NULL, + exchange_name character varying(255) NOT NULL, + description text, + engine character varying(20), + is_public boolean NOT NULL DEFAULT TRUE, + created_by character varying(36), + owned_by character varying(36), + contact_person character varying(36), + created timestamp NOT NULL DEFAULT NOW(), + last_modified timestamp, + PRIMARY KEY (id), + FOREIGN KEY (cid) REFERENCES mdb_containers (id) /* currently we only support one-to-one */, + FOREIGN KEY (created_by) REFERENCES mdb_users (id), + FOREIGN KEY (owned_by) REFERENCES mdb_users (id), + FOREIGN KEY (contact_person) REFERENCES mdb_users (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_databases_subjects` + ( + dbid BIGINT NOT NULL, + subjects character varying(255) NOT NULL, + PRIMARY KEY (dbid, subjects) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_tables` + ( + ID bigint NOT NULL AUTO_INCREMENT, + tDBID bigint NOT NULL, + internal_name character varying(255) NOT NULL, + queue_name character varying(255) NOT NULL, + routing_key character varying(255) NOT NULL, + tName VARCHAR(50), + tDescription TEXT, + NumCols INTEGER, + NumRows INTEGER, + `separator` CHAR(1), + quote CHAR(1), + element_null VARCHAR(50), + skip_lines BIGINT, + element_true VARCHAR(50), + element_false VARCHAR(50), + Version TEXT, + created timestamp NOT NULL DEFAULT NOW(), + versioned boolean not null default true, + created_by character varying(36) NOT NULL, + owned_by character varying(36) NOT NULL, + last_modified timestamp, + PRIMARY KEY (ID), + FOREIGN KEY (tDBID) REFERENCES mdb_databases (id), + FOREIGN KEY (created_by) REFERENCES mdb_users (id), + FOREIGN KEY (owned_by) REFERENCES mdb_users (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_columns` + ( + ID bigint NOT NULL AUTO_INCREMENT, + tID bigint NOT NULL, + dfID bigint, + cName VARCHAR(100), + internal_name VARCHAR(100) NOT NULL, + alias VARCHAR(100), + Datatype ENUM ('CHAR','VARCHAR','BINARY','VARBINARY','TINYBLOB','TINYTEXT','TEXT','BLOB','MEDIUMTEXT','MEDIUMBLOB','LONGTEXT','LONGBLOB','ENUM','SET','BIT','TINYINT','BOOL','SMALLINT','MEDIUMINT','INT','BIGINT','FLOAT','DOUBLE','DECIMAL','DATE','DATETIME','TIMESTAMP','TIME','YEAR'), + length INT NULL, + ordinal_position INTEGER NOT NULL, + is_primary_key BOOLEAN NOT NULL, + index_length INT NULL, + size INT, + d INT, + auto_generated BOOLEAN DEFAULT false, + is_null_allowed BOOLEAN NOT NULL DEFAULT true, + created timestamp NOT NULL DEFAULT NOW(), + last_modified timestamp, + FOREIGN KEY (tID) REFERENCES mdb_tables (ID), + PRIMARY KEY (ID) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_columns_enums` + ( + id bigint NOT NULL AUTO_INCREMENT, + column_id bigint NOT NULL, + value CHARACTER VARYING(255) NOT NULL, + FOREIGN KEY (column_id) REFERENCES mdb_columns (ID), + PRIMARY KEY (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_columns_sets` + ( + id bigint NOT NULL AUTO_INCREMENT, + column_id bigint NOT NULL, + value CHARACTER VARYING(255) NOT NULL, + FOREIGN KEY (column_id) REFERENCES mdb_columns (ID), + PRIMARY KEY (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_columns_nom` + ( + tID bigint, + cID bigint, + maxlength INTEGER, + last_modified timestamp, + created timestamp NOT NULL DEFAULT NOW(), + FOREIGN KEY (tID, cID) REFERENCES mdb_columns (tID, ID), + PRIMARY KEY (tID, cID) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_columns_num` + ( + tID bigint, + cID bigint, + SIunit TEXT, + MaxVal NUMERIC, + MinVal NUMERIC, + Mean NUMERIC, + Median NUMERIC, + Sd Numeric, + -- Histogram INTEGER[], + last_modified timestamp, + created timestamp NOT NULL DEFAULT NOW(), + FOREIGN KEY (tID, cID) REFERENCES mdb_columns (tID, ID), + PRIMARY KEY (tID, cID) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_columns_cat` + ( + tID bigint, + cID bigint, + num_cat INTEGER, + -- cat_array TEXT[], + last_modified timestamp, + created timestamp NOT NULL DEFAULT NOW(), + FOREIGN KEY (tID, cID) REFERENCES mdb_columns (tID, ID), + PRIMARY KEY (tID, cID) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_constraints_foreign_key` + ( + fkid BIGINT NOT NULL AUTO_INCREMENT, + tid BIGINT NOT NULL, + rtid BIGINT NOT NULL, + on_update VARCHAR(50) NULL, + on_delete VARCHAR(50) NULL, + position INT NULL, + PRIMARY KEY (fkid), + FOREIGN KEY (tid) REFERENCES mdb_tables (id), + FOREIGN KEY (rtid) REFERENCES mdb_tables (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_constraints_foreign_key_reference` + ( + id BIGINT NOT NULL AUTO_INCREMENT, + fkid BIGINT NOT NULL, + cid BIGINT NOT NULL, + rcid BIGINT NOT NULL, + PRIMARY KEY (id), + FOREIGN KEY (fkid) REFERENCES mdb_constraints_foreign_key (fkid) ON UPDATE CASCADE, + FOREIGN KEY (cid) REFERENCES mdb_columns (id), + FOREIGN KEY (rcid) REFERENCES mdb_columns (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_constraints_unique` + ( + uid BIGINT NOT NULL AUTO_INCREMENT, + tid BIGINT NOT NULL, + position INT NULL, + PRIMARY KEY (uid), + FOREIGN KEY (tid) REFERENCES mdb_tables (id) + ); + + CREATE TABLE IF NOT EXISTS `mdb_constraints_unique_columns` + ( + id BIGINT NOT NULL AUTO_INCREMENT, + uid BIGINT NOT NULL, + cid BIGINT NOT NULL, + PRIMARY KEY (id), + FOREIGN KEY (uid) REFERENCES mdb_constraints_unique (uid), + FOREIGN KEY (cid) REFERENCES mdb_columns (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_constraints_checks` + ( + id BIGINT NOT NULL AUTO_INCREMENT, + tid BIGINT NOT NULL, + checks VARCHAR(255) NOT NULL, + PRIMARY KEY (id), + FOREIGN KEY (tid) REFERENCES mdb_tables (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_concepts` + ( + id bigint NOT NULL AUTO_INCREMENT, + uri text not null, + name VARCHAR(255) null, + description TEXT null, + created timestamp NOT NULL DEFAULT NOW(), + PRIMARY KEY (id), + UNIQUE (uri(200)) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_units` + ( + id bigint NOT NULL AUTO_INCREMENT, + uri text not null, + name VARCHAR(255) null, + description TEXT null, + created timestamp NOT NULL DEFAULT NOW(), + PRIMARY KEY (id), + UNIQUE (uri(200)) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_columns_concepts` + ( + id bigint NOT NULL, + cID bigint NOT NULL, + created timestamp NOT NULL DEFAULT NOW(), + PRIMARY KEY (id), + FOREIGN KEY (cID) REFERENCES mdb_columns (ID) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_columns_units` + ( + id bigint NOT NULL, + cID bigint NOT NULL, + created timestamp NOT NULL DEFAULT NOW(), + PRIMARY KEY (id), + FOREIGN KEY (cID) REFERENCES mdb_columns (ID) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_view` + ( + id bigint NOT NULL AUTO_INCREMENT, + vdbid bigint NOT NULL, + vName VARCHAR(255) NOT NULL, + internal_name VARCHAR(255) NOT NULL, + Query TEXT NOT NULL, + query_hash VARCHAR(255) NOT NULL, + Public BOOLEAN NOT NULL, + NumCols INTEGER, + NumRows INTEGER, + InitialView BOOLEAN NOT NULL, + created timestamp NOT NULL DEFAULT NOW(), + last_modified timestamp, + created_by character varying(36) NOT NULL, + PRIMARY KEY (id), + FOREIGN KEY (vdbid) REFERENCES mdb_databases (id), + FOREIGN KEY (created_by) REFERENCES mdb_users (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_banner_messages` + ( + id bigint NOT NULL AUTO_INCREMENT, + type ENUM ('ERROR', 'WARNING', 'INFO') NOT NULL default 'INFO', + message TEXT NOT NULL, + link TEXT NULL, + link_text VARCHAR(255) NULL, + display_start timestamp NULL, + display_end timestamp NULL, + PRIMARY KEY (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_ontologies` + ( + id bigint NOT NULL AUTO_INCREMENT, + prefix VARCHAR(8) NOT NULL, + uri TEXT NOT NULL, + uri_pattern TEXT, + sparql_endpoint TEXT NULL, + last_modified timestamp, + created timestamp NOT NULL DEFAULT NOW(), + UNIQUE (prefix), + UNIQUE (uri(200)), + PRIMARY KEY (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_view_columns` + ( + id BIGINT NOT NULL AUTO_INCREMENT, + cid BIGINT NOT NULL, + vid BIGINT NOT NULL, + position INTEGER NULL, + PRIMARY KEY (id), + FOREIGN KEY (vid) REFERENCES mdb_view (id), + FOREIGN KEY (cid) REFERENCES mdb_columns (ID) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_identifiers` + ( + id bigint NOT NULL AUTO_INCREMENT, + dbid bigint, + qid bigint, + vid bigint, + publisher VARCHAR(255) NOT NULL, + language VARCHAR(2), + visibility ENUM ('SELF', 'EVERYONE') NOT NULL default 'EVERYONE', + publication_year INTEGER NOT NULL, + publication_month INTEGER, + publication_day INTEGER, + identifier_type ENUM ('DATABASE', 'SUBSET', 'VIEW') NOT NULL, + query TEXT, + query_normalized TEXT, + query_hash VARCHAR(255), + execution timestamp, + result_hash VARCHAR(255), + result_number bigint, + doi VARCHAR(255), + created timestamp NOT NULL DEFAULT NOW(), + created_by character varying(36) NOT NULL, + last_modified timestamp, + PRIMARY KEY (id), /* must be a single id from persistent identifier concept */ + FOREIGN KEY (dbid) REFERENCES mdb_databases (id), + UNIQUE (dbid, qid), + FOREIGN KEY (created_by) REFERENCES mdb_users (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_identifier_licenses` + ( + pid bigint NOT NULL, + license_id VARCHAR(255) NOT NULL, + PRIMARY KEY (pid, license_id), + FOREIGN KEY (pid) REFERENCES mdb_identifiers (id), + FOREIGN KEY (license_id) REFERENCES mdb_licenses (identifier) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_identifier_titles` + ( + id bigint NOT NULL AUTO_INCREMENT, + pid bigint NOT NULL, + title text NOT NULL, + title_type ENUM ('ALTERNATIVE_TITLE', 'SUBTITLE', 'TRANSLATED_TITLE', 'OTHER'), + language VARCHAR(2), + PRIMARY KEY (id), + FOREIGN KEY (pid) REFERENCES mdb_identifiers (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_identifier_funders` + ( + id bigint NOT NULL AUTO_INCREMENT, + pid bigint NOT NULL, + funder_name VARCHAR(255) NOT NULL, + funder_identifier TEXT, + funder_identifier_type ENUM ('CROSSREF_FUNDER_ID', 'GRID', 'ISNI', 'ROR', 'OTHER'), + scheme_uri text, + award_number VARCHAR(255), + award_title text, + language VARCHAR(255), + PRIMARY KEY (id), + FOREIGN KEY (pid) REFERENCES mdb_identifiers (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_identifier_descriptions` + ( + id bigint NOT NULL AUTO_INCREMENT, + pid bigint NOT NULL, + description text NOT NULL, + description_type ENUM ('ABSTRACT', 'METHODS', 'SERIES_INFORMATION', 'TABLE_OF_CONTENTS', 'TECHNICAL_INFO', 'OTHER'), + language VARCHAR(2), + PRIMARY KEY (id), + FOREIGN KEY (pid) REFERENCES mdb_identifiers (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_related_identifiers` + ( + id bigint NOT NULL AUTO_INCREMENT, + pid bigint NOT NULL, + value varchar(255) NOT NULL, + type varchar(255), + relation varchar(255), + PRIMARY KEY (id), /* must be a single id from persistent identifier concept */ + FOREIGN KEY (pid) REFERENCES mdb_identifiers (id), + UNIQUE (pid, value) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_identifier_creators` + ( + id bigint NOT NULL AUTO_INCREMENT, + pid bigint NOT NULL, + given_names text, + family_name text, + creator_name VARCHAR(255) NOT NULL, + name_type ENUM ('PERSONAL', 'ORGANIZATIONAL') default 'PERSONAL', + name_identifier text, + name_identifier_scheme ENUM ('ROR', 'GRID', 'ISNI', 'ORCID'), + name_identifier_scheme_uri text, + affiliation VARCHAR(255), + affiliation_identifier text, + affiliation_identifier_scheme ENUM ('ROR', 'GRID', 'ISNI'), + affiliation_identifier_scheme_uri text, + PRIMARY KEY (id), + FOREIGN KEY (pid) REFERENCES mdb_identifiers (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_feed` + ( + fDBID bigint, + fID bigint, + fUserId character varying(36) not null, + fDataID bigint REFERENCES mdb_data (ID), + created timestamp NOT NULL DEFAULT NOW(), + PRIMARY KEY (fDBID, fID, fUserId, fDataID), + FOREIGN KEY (fDBID, fID) REFERENCES mdb_tables (tDBID, ID), + FOREIGN KEY (fUserId) REFERENCES mdb_users (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_update` + ( + uUserID character varying(255) NOT NULL, + uDBID bigint NOT NULL, + created timestamp NOT NULL DEFAULT NOW(), + PRIMARY KEY (uUserID, uDBID), + FOREIGN KEY (uDBID) REFERENCES mdb_databases (id) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_access` + ( + aUserID character varying(255) NOT NULL, + aDBID bigint REFERENCES mdb_databases (id), + attime TIMESTAMP, + download BOOLEAN, + created timestamp NOT NULL DEFAULT NOW(), + PRIMARY KEY (aUserID, aDBID) + ) WITH SYSTEM VERSIONING; + + CREATE TABLE IF NOT EXISTS `mdb_have_access` + ( + user_id character varying(36) NOT NULL, + database_id bigint REFERENCES mdb_databases (id), + access_type ENUM ('READ', 'WRITE_OWN', 'WRITE_ALL') NOT NULL, + created timestamp NOT NULL DEFAULT NOW(), + PRIMARY KEY (user_id, database_id), + FOREIGN KEY (user_id) REFERENCES mdb_users (id) + ) WITH SYSTEM VERSIONING; + + COMMIT; + BEGIN; + + INSERT INTO `mdb_licenses` (identifier, uri) + VALUES ('MIT', 'https://opensource.org/licenses/MIT'), + ('GPL-3.0-only', 'https://www.gnu.org/licenses/gpl-3.0-standalone.html'), + ('BSD-3-Clause', 'https://opensource.org/licenses/BSD-3-Clause'), + ('BSD-4-Clause', 'http://directory.fsf.org/wiki/License:BSD_4Clause'), + ('Apache-2.0', 'https://opensource.org/licenses/Apache-2.0'), + ('CC0-1.0', 'https://creativecommons.org/publicdomain/zero/1.0/legalcode'), + ('CC-BY-4.0', 'https://creativecommons.org/licenses/by/4.0/legalcode'); + + INSERT INTO `mdb_images` (name, version, default_port, dialect, driver_class, jdbc_method) + VALUES ('mariadb', '11.1.3', 3306, 'org.hibernate.dialect.MariaDBDialect', 'org.mariadb.jdbc.Driver', 'mariadb'); + + INSERT INTO `mdb_images_date` (iid, database_format, unix_format, example, has_time) + VALUES (1, '%Y-%c-%d %H:%i:%S.%f', 'yyyy-MM-dd HH:mm:ss.SSSSSS', '2022-01-30 13:44:25.499', true), + (1, '%Y-%c-%d %H:%i:%S', 'yyyy-MM-dd HH:mm:ss', '2022-01-30 13:44:25', true), + (1, '%Y-%c-%d', 'yyyy-MM-dd', '2022-01-30', false), + (1, '%H:%i:%S', 'HH:mm:ss', '13:44:25', true); + + INSERT INTO `mdb_ontologies` (prefix, uri, uri_pattern, sparql_endpoint) + VALUES ('om', 'http://www.ontology-of-units-of-measure.org/resource/om-2/', + 'http://www.ontology-of-units-of-measure.org/resource/om-2/.*', null), + ('wd', 'http://www.wikidata.org/', 'http://www.wikidata.org/entity/.*', 'https://query.wikidata.org/sparql'), + ('mo', 'http://purl.org/ontology/mo/', 'http://purl.org/ontology/mo/.*', null), + ('dc', 'http://purl.org/dc/elements/1.1/', null, null), + ('xsd', 'http://www.w3.org/2001/XMLSchema#', null, null), + ('tl', 'http://purl.org/NET/c4dm/timeline.owl#', null, null), + ('foaf', 'http://xmlns.com/foaf/0.1/', null, null), + ('schema', 'http://schema.org/', null, null), + ('rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', null, null), + ('rdfs', 'http://www.w3.org/2000/01/rdf-schema#', null, null), + ('owl', 'http://www.w3.org/2002/07/owl#', null, null), + ('prov', 'http://www.w3.org/ns/prov#', null, null), + ('db', 'http://dbpedia.org', 'http://dbpedia.org/ontology/.*', 'http://dbpedia.org/sparql'); + COMMIT; diff --git a/dbrepo-metadata-db/setup-schema_local.sql b/dbrepo-metadata-db/setup-schema_local.sql index c7d132bc0c..1c144e31e3 100644 --- a/dbrepo-metadata-db/setup-schema_local.sql +++ b/dbrepo-metadata-db/setup-schema_local.sql @@ -2,6 +2,6 @@ BEGIN; INSERT INTO `mdb_containers` (name, internal_name, image_id, host, port, sidecar_host, sidecar_port, privileged_username, privileged_password) -VALUES ('MariaDB 10.5', 'mariadb_10_5', 1, 'data-db', 3306, 'data-db-sidecar', 3305, 'root', 'dbrepo'); +VALUES ('MariaDB 11.1.3', 'mariadb_11_1_3', 1, 'data-db', 3306, 'data-db-sidecar', 3305, 'root', 'dbrepo'); COMMIT; diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/QueryMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/QueryMapper.java index f02e38456f..a96053ee95 100644 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/QueryMapper.java +++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/QueryMapper.java @@ -157,7 +157,7 @@ public interface QueryMapper { } default PreparedStatement pathToRawInsertQuery(Connection connection, Table table, ImportDto data) throws QueryMalformedException { - final StringBuilder statement = new StringBuilder("LOAD DATA LOCAL INFILE '/tmp/") + final StringBuilder statement = new StringBuilder("LOAD DATA INFILE '/tmp/") .append(data.getLocation()) .append("' INTO TABLE `") .append(table.getDatabase().getInternalName()) diff --git a/dbrepo-ui/api/index.js b/dbrepo-ui/api/index.js index 9fb169e7b5..3050d758e8 100644 --- a/dbrepo-ui/api/index.js +++ b/dbrepo-ui/api/index.js @@ -1,8 +1,8 @@ import axios from 'axios' -import config from '../dbrepo.config.json' -const protocol = config.api.useSsl ? 'https' : 'http' -const baseUrl = `${protocol}://${config.api.endpoint}:${config.api.port}` +const baseUrl = `${location.protocol}//${location.host}` + +console.debug('base url', baseUrl) const instance = axios.create({ timeout: 10000, diff --git a/dbrepo-ui/api/upload.service.js b/dbrepo-ui/api/upload.service.js index c5499bbc07..f37cf24d1f 100644 --- a/dbrepo-ui/api/upload.service.js +++ b/dbrepo-ui/api/upload.service.js @@ -1,19 +1,18 @@ import Vue from 'vue' -import config from '../dbrepo.config' const tus = require('tus-js-client') class UploadService { upload (file) { return new Promise((resolve, reject) => { - const protocol = config.api.useSsl ? 'https' : 'http' - const baseUrl = `${protocol}://${config.api.endpoint}:${config.api.port}` + const endpoint = `${location.protocol}//${location.host}/api/upload/files` + console.debug('upload endpoint', endpoint) if (!tus.isSupported) { console.error('Your browser does not support uploads!') Vue.$toast.error('Your browser does not support uploads!') return } const upload = new tus.Upload(file, { - endpoint: `${baseUrl}/api/upload/files`, + endpoint, retryDelays: [0, 3000, 5000, 10000, 20000], metadata: { filename: file.name, diff --git a/dbrepo-ui/dbrepo.config.json b/dbrepo-ui/dbrepo.config.json index 007198b35d..e908ff37ab 100644 --- a/dbrepo-ui/dbrepo.config.json +++ b/dbrepo-ui/dbrepo.config.json @@ -11,13 +11,10 @@ "path": "/favicon.ico" }, "api": { - "endpoint": "localhost", - "port": 80, "useSsl": false }, "broker": { "connection": { - "host": "localhost", "ports": [ 5672 ], @@ -25,6 +22,9 @@ } }, "storage": { + "endpoint": "storage-service", + "port": 9000, + "useSsl": false, "accessKey": { "id": "minioadmin", "secret": "minioadmin" diff --git a/dbrepo-ui/nuxt.config.js b/dbrepo-ui/nuxt.config.js index 5b2c854f87..0476e44fc7 100644 --- a/dbrepo-ui/nuxt.config.js +++ b/dbrepo-ui/nuxt.config.js @@ -4,9 +4,8 @@ import config from './dbrepo.config.json' const proxy = {} -const api = 'http://localhost' - if (process.env.NODE_ENV === 'development') { + const api = 'http://localhost' proxy['/api'] = api proxy['/pid'] = { target: api + '/api', diff --git a/dbrepo-ui/pages/database/_database_id/table/import.vue b/dbrepo-ui/pages/database/_database_id/table/import.vue index fdcf96391e..5501a4929b 100644 --- a/dbrepo-ui/pages/database/_database_id/table/import.vue +++ b/dbrepo-ui/pages/database/_database_id/table/import.vue @@ -272,14 +272,6 @@ export default { token () { return this.$store.state.token }, - config () { - if (this.token === null) { - return {} - } - return { - headers: { Authorization: `Bearer ${this.token}` } - } - }, user () { return this.$store.state.user }, diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 58e2f0cea5..f27ba6337f 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -39,7 +39,7 @@ services: restart: "no" container_name: dbrepo-data-db hostname: data-db - image: docker.io/bitnami/mariadb:10.5 + image: docker.io/bitnami/mariadb:11.1.3 volumes: - data-db-data:/bitnami/mariadb - "${SHARED_FILESYSTEM:-/tmp}:/tmp" @@ -59,7 +59,7 @@ services: restart: "no" container_name: dbrepo-auth-db hostname: auth-db - image: docker.io/bitnami/mariadb:10.5 + image: docker.io/bitnami/mariadb:11.1.3 volumes: - auth-db-data:/bitnami/mariadb ports: diff --git a/docker-compose.yml b/docker-compose.yml index c0074047d7..4074c172ab 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,7 +40,7 @@ services: restart: "no" container_name: dbrepo-data-db hostname: data-db - image: docker.io/bitnami/mariadb:10.5 + image: docker.io/bitnami/mariadb:11.1.3 volumes: - data-db-data:/bitnami/mariadb - "${SHARED_FILESYSTEM:-/tmp}:/tmp" @@ -60,7 +60,7 @@ services: restart: "no" container_name: dbrepo-auth-db hostname: auth-db - image: docker.io/bitnami/mariadb:10.5 + image: docker.io/bitnami/mariadb:11.1.3 volumes: - auth-db-data:/bitnami/mariadb ports: @@ -281,6 +281,8 @@ services: hostname: ui image: dbrepo-ui:latest build: ./dbrepo-ui + volumes: + - ./dbrepo-ui/dbrepo.config.json:/app/dbrepo.config.json depends_on: dbrepo-search-service: condition: service_started diff --git a/mkdocs.yml b/mkdocs.yml index a632fe06c9..2e236a5dc4 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,11 +7,8 @@ docs_dir: .docs nav: - Home: index.md - Deployment: + - Kubernetes: deployment-helm.md - Docker Compose: deployment-docker-compose.md - - Kubernetes: - - Helm Chart: deployment-helm.md - - "Special: Minikube": deployment-kubernetes-minikube.md - - "Special: Azure Cloud": deployment-kubernetes-azure.md - System: - Overview: system.md - Services: -- GitLab