This is page 2 of 7. Use http://codebase.md/chillbruhhh/crawl4ai-mcp?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .dockerignore
├── .env.example
├── .gitattributes
├── .gitignore
├── crawled_pages.sql
├── Dockerfile
├── knowledge_graphs
│ ├── ai_hallucination_detector.py
│ ├── ai_script_analyzer.py
│ ├── hallucination_reporter.py
│ ├── knowledge_graph_validator.py
│ ├── parse_repo_into_neo4j.py
│ ├── query_knowledge_graph.py
│ └── test_script.py
├── LICENSE
├── neo4j
│ └── docker-neo4j
│ ├── .github
│ │ └── ISSUE_TEMPLATE
│ │ └── bug_report.md
│ ├── .gitignore
│ ├── build-docker-image.sh
│ ├── build-utils-common-functions.sh
│ ├── COPYRIGHT
│ ├── DEVELOPMENT.md
│ ├── devenv
│ ├── devenv.local.template
│ ├── docker-image-src
│ │ ├── 2.3
│ │ │ ├── docker-entrypoint.sh
│ │ │ └── Dockerfile
│ │ ├── 3.0
│ │ │ ├── docker-entrypoint.sh
│ │ │ └── Dockerfile
│ │ ├── 3.1
│ │ │ ├── docker-entrypoint.sh
│ │ │ └── Dockerfile
│ │ ├── 3.2
│ │ │ ├── docker-entrypoint.sh
│ │ │ └── Dockerfile
│ │ ├── 3.3
│ │ │ ├── docker-entrypoint.sh
│ │ │ └── Dockerfile
│ │ ├── 3.4
│ │ │ ├── docker-entrypoint.sh
│ │ │ └── Dockerfile
│ │ ├── 3.5
│ │ │ ├── coredb
│ │ │ │ ├── docker-entrypoint.sh
│ │ │ │ ├── Dockerfile
│ │ │ │ └── neo4j-plugins.json
│ │ │ └── neo4j-admin
│ │ │ ├── docker-entrypoint.sh
│ │ │ └── Dockerfile
│ │ ├── 4.0
│ │ │ ├── coredb
│ │ │ │ ├── docker-entrypoint.sh
│ │ │ │ └── Dockerfile
│ │ │ └── neo4j-admin
│ │ │ ├── docker-entrypoint.sh
│ │ │ └── Dockerfile
│ │ ├── 4.1
│ │ │ ├── coredb
│ │ │ │ ├── docker-entrypoint.sh
│ │ │ │ └── Dockerfile
│ │ │ └── neo4j-admin
│ │ │ ├── docker-entrypoint.sh
│ │ │ └── Dockerfile
│ │ ├── 4.2
│ │ │ ├── coredb
│ │ │ │ ├── docker-entrypoint.sh
│ │ │ │ ├── Dockerfile
│ │ │ │ └── neo4j-plugins.json
│ │ │ └── neo4j-admin
│ │ │ ├── docker-entrypoint.sh
│ │ │ └── Dockerfile
│ │ ├── 4.3
│ │ │ ├── coredb
│ │ │ │ ├── docker-entrypoint.sh
│ │ │ │ ├── Dockerfile
│ │ │ │ └── neo4j-plugins.json
│ │ │ └── neo4j-admin
│ │ │ ├── docker-entrypoint.sh
│ │ │ └── Dockerfile
│ │ ├── 4.4
│ │ │ ├── coredb
│ │ │ │ ├── docker-entrypoint.sh
│ │ │ │ ├── Dockerfile-debian
│ │ │ │ ├── Dockerfile-ubi9
│ │ │ │ ├── neo4j-admin-report.sh
│ │ │ │ └── neo4j-plugins.json
│ │ │ └── neo4j-admin
│ │ │ ├── docker-entrypoint.sh
│ │ │ ├── Dockerfile-debian
│ │ │ └── Dockerfile-ubi9
│ │ ├── 5
│ │ │ ├── coredb
│ │ │ │ ├── docker-entrypoint.sh
│ │ │ │ ├── Dockerfile-debian
│ │ │ │ ├── Dockerfile-ubi8
│ │ │ │ ├── Dockerfile-ubi9
│ │ │ │ ├── neo4j-admin-report.sh
│ │ │ │ └── neo4j-plugins.json
│ │ │ └── neo4j-admin
│ │ │ ├── docker-entrypoint.sh
│ │ │ ├── Dockerfile-debian
│ │ │ ├── Dockerfile-ubi8
│ │ │ └── Dockerfile-ubi9
│ │ ├── calver
│ │ │ ├── coredb
│ │ │ │ ├── docker-entrypoint.sh
│ │ │ │ ├── Dockerfile-debian
│ │ │ │ ├── Dockerfile-ubi9
│ │ │ │ ├── neo4j-admin-report.sh
│ │ │ │ └── neo4j-plugins.json
│ │ │ └── neo4j-admin
│ │ │ ├── docker-entrypoint.sh
│ │ │ ├── Dockerfile-debian
│ │ │ └── Dockerfile-ubi9
│ │ └── common
│ │ ├── semver.jq
│ │ └── utilities.sh
│ ├── generate-stub-plugin
│ │ ├── build.gradle.kts
│ │ ├── Dockerfile
│ │ ├── ExampleNeo4jPlugin.java
│ │ ├── Makefile
│ │ ├── README.md
│ │ └── settings.gradle.kts
│ ├── LICENSE
│ ├── Makefile
│ ├── pom.xml
│ ├── publish-neo4j-admin-image.sh
│ ├── publish-neo4j-admin-images.sh
│ ├── README.md
│ └── src
│ ├── main
│ │ └── resources
│ │ └── log4j.properties
│ └── test
│ ├── java
│ │ └── com
│ │ └── neo4j
│ │ └── docker
│ │ ├── coredb
│ │ │ ├── configurations
│ │ │ │ ├── Configuration.java
│ │ │ │ ├── Setting.java
│ │ │ │ ├── TestConfSettings.java
│ │ │ │ ├── TestExtendedConf.java
│ │ │ │ └── TestJVMAdditionalConfig.java
│ │ │ ├── plugins
│ │ │ │ ├── Neo4jPluginEnv.java
│ │ │ │ ├── StubPluginHelper.java
│ │ │ │ ├── TestBundledPluginInstallation.java
│ │ │ │ ├── TestPluginInstallation.java
│ │ │ │ └── TestSemVerPluginMatching.java
│ │ │ ├── TestAdminReport.java
│ │ │ ├── TestAuthentication.java
│ │ │ ├── TestBasic.java
│ │ │ ├── TestCausalCluster.java
│ │ │ ├── TestMounting.java
│ │ │ └── TestUpgrade.java
│ │ ├── neo4jadmin
│ │ │ ├── TestAdminBasic.java
│ │ │ ├── TestBackupRestore.java
│ │ │ ├── TestBackupRestore44.java
│ │ │ ├── TestDumpLoad.java
│ │ │ ├── TestDumpLoad44.java
│ │ │ └── TestReport.java
│ │ ├── TestDeprecationWarning.java
│ │ ├── TestDockerComposeSecrets.java
│ │ └── utils
│ │ ├── DatabaseIO.java
│ │ ├── HostFileHttpHandler.java
│ │ ├── HttpServerTestExtension.java
│ │ ├── Neo4jVersion.java
│ │ ├── Neo4jVersionTest.java
│ │ ├── Network.java
│ │ ├── SetContainerUser.java
│ │ ├── TemporaryFolderManager.java
│ │ ├── TemporaryFolderManagerTest.java
│ │ ├── TestSettings.java
│ │ └── WaitStrategies.java
│ └── resources
│ ├── causal-cluster-compose.yml
│ ├── confs
│ │ ├── before50
│ │ │ ├── ConfsNotOverridden.conf
│ │ │ ├── ConfsReplaced.conf
│ │ │ ├── EnterpriseOnlyNotOverwritten.conf
│ │ │ ├── EnvVarsOverride.conf
│ │ │ ├── ExtendedConf.conf
│ │ │ ├── InvalidExtendedConf.conf
│ │ │ ├── JvmAdditionalNotOverridden.conf
│ │ │ ├── NoNewline.conf
│ │ │ └── ReadConf.conf
│ │ ├── ConfsNotOverridden.conf
│ │ ├── ConfsReplaced.conf
│ │ ├── EnterpriseOnlyNotOverwritten.conf
│ │ ├── EnvVarsOverride.conf
│ │ ├── ExtendedConf.conf
│ │ ├── InvalidExtendedConf.conf
│ │ ├── JvmAdditionalNotOverridden.conf
│ │ ├── NoNewline.conf
│ │ └── ReadConf.conf
│ ├── dockersecrets
│ │ ├── container-compose-with-incorrect-secrets.yml
│ │ ├── container-compose-with-secrets-override.yml
│ │ ├── container-compose-with-secrets.yml
│ │ ├── simple-container-compose-with-external-file-var.yml
│ │ └── simple-container-compose.yml
│ ├── ha-cluster-compose.yml
│ └── stubplugin
│ └── myPlugin.jar
├── pyproject.toml
├── README.md
├── src
│ ├── crawl4ai_mcp.py
│ └── utils.py
└── uv.lock
```
# Files
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/docker-image-src/4.0/neo4j-admin/docker-entrypoint.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash -eu
2 |
3 | function running_as_root
4 | {
5 | test "$(id -u)" = "0"
6 | }
7 |
8 | function is_writable
9 | {
10 | gosu "${userid}":"${groupid}" test -w "${1}"
11 | }
12 |
13 | function print_permissions_advice_and_fail
14 | {
15 | local _directory=${1}
16 | echo >&2 "
17 | Folder ${_directory} is not accessible for user: ${userid} or group ${groupid} or groups ${groups[@]}, this is commonly a file permissions issue on the mounted folder.
18 |
19 | Hints to solve the issue:
20 | 1) Make sure the folder exists before mounting it. Docker will create the folder using root permissions before starting the Neo4j container. The root permissions disallow Neo4j from writing to the mounted folder.
21 | 2) Pass the folder owner's user ID and group ID to docker run, so that docker runs as that user.
22 | If the folder is owned by the current user, this can be done by adding this flag to your docker run command:
23 | --user=\$(id -u):\$(id -g)
24 | "
25 | exit 1
26 | }
27 |
28 |
29 | function check_mounted_folder_writable_with_chown
30 | {
31 | local mountFolder=${1}
32 | if running_as_root; then
33 | # check folder permissions
34 | if ! is_writable "${mountFolder}" ; then
35 | # warn that we're about to chown the folder and then chown it
36 | echo "Warning: Folder mounted to \"${mountFolder}\" is not writable from inside container. Changing folder owner to ${userid}."
37 | chown -R "${userid}":"${groupid}" "${mountFolder}"
38 | # check permissions on files in the folder
39 | elif [ $(gosu "${userid}":"${groupid}" find "${mountFolder}" -not -writable | wc -l) -gt 0 ]; then
40 | echo "Warning: Some files inside \"${mountFolder}\" are not writable from inside container. Changing folder owner to ${userid}."
41 | chown -R "${userid}":"${groupid}" "${mountFolder}"
42 | fi
43 | else
44 | if [[ ! -w "${mountFolder}" ]] && [[ "$(stat -c %U ${mountFolder})" != "neo4j" ]]; then
45 | print_permissions_advice_and_fail "${mountFolder}"
46 | fi
47 | fi
48 | }
49 |
50 | # ==== SETUP WHICH USER TO RUN AS ====
51 |
52 | if running_as_root; then
53 | userid="neo4j"
54 | groupid="neo4j"
55 | groups=($(id -G neo4j))
56 | exec_cmd="exec gosu neo4j:neo4j"
57 | else
58 | userid="$(id -u)"
59 | groupid="$(id -g)"
60 | groups=($(id -G))
61 | exec_cmd="exec"
62 | fi
63 |
64 |
65 | # ==== CHECK LICENSE AGREEMENT ====
66 |
67 | if [ "${NEO4J_EDITION}" == "enterprise" ]; then
68 | if [ "${NEO4J_ACCEPT_LICENSE_AGREEMENT:=no}" != "yes" ]; then
69 | echo >&2 "
70 | In order to use Neo4j Enterprise Edition you must accept the license agreement.
71 |
72 | (c) Neo4j Sweden AB. 2021. All Rights Reserved.
73 | Use of this Software without a proper commercial license with Neo4j,
74 | Inc. or its affiliates is prohibited.
75 |
76 | Email inquiries can be directed to: [email protected]
77 |
78 | More information is also available at: https://neo4j.com/licensing/
79 |
80 |
81 | To accept the license agreement set the environment variable
82 | NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
83 |
84 | To do this you can use the following docker argument:
85 |
86 | --env=NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
87 | "
88 | exit 1
89 | fi
90 | fi
91 |
92 | # ==== ENSURE MOUNT FOLDER READ/WRITABILITY ====
93 |
94 | if [ -d /data ]; then
95 | check_mounted_folder_writable_with_chown "/data"
96 | if [ -d /data/databases ]; then
97 | check_mounted_folder_writable_with_chown "/data/databases"
98 | fi
99 | if [ -d /data/dbms ]; then
100 | check_mounted_folder_writable_with_chown "/data/dbms"
101 | fi
102 | if [ -d /data/transactions ]; then
103 | check_mounted_folder_writable_with_chown "/data/transactions"
104 | fi
105 | fi
106 | if [ -d /backups ]; then
107 | check_mounted_folder_writable_with_chown "/backups"
108 | fi
109 |
110 | # ==== MAKE SURE NEO4J CANNOT BE RUN FROM THIS CONTAINER ====
111 |
112 | rm ${NEO4J_HOME}/bin/neo4j
113 |
114 | if [[ "${1}" == "neo4j" ]]; then
115 | correct_image="neo4j:"$(neo4j-admin --version)"-${NEO4J_EDITION}"
116 | echo >&2 "
117 | This is a neo4j-admin only image, and usage of Neo4j server is not supported from here.
118 | If you wish to start a Neo4j database, use:
119 |
120 | docker run ${correct_image}
121 | "
122 | exit 1
123 | fi
124 |
125 | # ==== START NEO4J-ADMIN COMMAND ====
126 |
127 | ${exec_cmd} "${@}"
128 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/docker-image-src/4.1/neo4j-admin/docker-entrypoint.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash -eu
2 |
3 | function running_as_root
4 | {
5 | test "$(id -u)" = "0"
6 | }
7 |
8 | function is_writable
9 | {
10 | gosu "${userid}":"${groupid}" test -w "${1}"
11 | }
12 |
13 | function print_permissions_advice_and_fail
14 | {
15 | local _directory=${1}
16 | echo >&2 "
17 | Folder ${_directory} is not accessible for user: ${userid} or group ${groupid} or groups ${groups[@]}, this is commonly a file permissions issue on the mounted folder.
18 |
19 | Hints to solve the issue:
20 | 1) Make sure the folder exists before mounting it. Docker will create the folder using root permissions before starting the Neo4j container. The root permissions disallow Neo4j from writing to the mounted folder.
21 | 2) Pass the folder owner's user ID and group ID to docker run, so that docker runs as that user.
22 | If the folder is owned by the current user, this can be done by adding this flag to your docker run command:
23 | --user=\$(id -u):\$(id -g)
24 | "
25 | exit 1
26 | }
27 |
28 |
29 | function check_mounted_folder_writable_with_chown
30 | {
31 | local mountFolder=${1}
32 | if running_as_root; then
33 | # check folder permissions
34 | if ! is_writable "${mountFolder}" ; then
35 | # warn that we're about to chown the folder and then chown it
36 | echo "Warning: Folder mounted to \"${mountFolder}\" is not writable from inside container. Changing folder owner to ${userid}."
37 | chown -R "${userid}":"${groupid}" "${mountFolder}"
38 | # check permissions on files in the folder
39 | elif [ $(gosu "${userid}":"${groupid}" find "${mountFolder}" -not -writable | wc -l) -gt 0 ]; then
40 | echo "Warning: Some files inside \"${mountFolder}\" are not writable from inside container. Changing folder owner to ${userid}."
41 | chown -R "${userid}":"${groupid}" "${mountFolder}"
42 | fi
43 | else
44 | if [[ ! -w "${mountFolder}" ]] && [[ "$(stat -c %U ${mountFolder})" != "neo4j" ]]; then
45 | print_permissions_advice_and_fail "${mountFolder}"
46 | fi
47 | fi
48 | }
49 |
50 | # ==== SETUP WHICH USER TO RUN AS ====
51 |
52 | if running_as_root; then
53 | userid="neo4j"
54 | groupid="neo4j"
55 | groups=($(id -G neo4j))
56 | exec_cmd="exec gosu neo4j:neo4j"
57 | else
58 | userid="$(id -u)"
59 | groupid="$(id -g)"
60 | groups=($(id -G))
61 | exec_cmd="exec"
62 | fi
63 |
64 |
65 | # ==== CHECK LICENSE AGREEMENT ====
66 |
67 | if [ "${NEO4J_EDITION}" == "enterprise" ]; then
68 | if [ "${NEO4J_ACCEPT_LICENSE_AGREEMENT:=no}" != "yes" ]; then
69 | echo >&2 "
70 | In order to use Neo4j Enterprise Edition you must accept the license agreement.
71 |
72 | (c) Neo4j Sweden AB. 2021. All Rights Reserved.
73 | Use of this Software without a proper commercial license with Neo4j,
74 | Inc. or its affiliates is prohibited.
75 |
76 | Email inquiries can be directed to: [email protected]
77 |
78 | More information is also available at: https://neo4j.com/licensing/
79 |
80 |
81 | To accept the license agreement set the environment variable
82 | NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
83 |
84 | To do this you can use the following docker argument:
85 |
86 | --env=NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
87 | "
88 | exit 1
89 | fi
90 | fi
91 |
92 | # ==== ENSURE MOUNT FOLDER READ/WRITABILITY ====
93 |
94 | if [ -d /data ]; then
95 | check_mounted_folder_writable_with_chown "/data"
96 | if [ -d /data/databases ]; then
97 | check_mounted_folder_writable_with_chown "/data/databases"
98 | fi
99 | if [ -d /data/dbms ]; then
100 | check_mounted_folder_writable_with_chown "/data/dbms"
101 | fi
102 | if [ -d /data/transactions ]; then
103 | check_mounted_folder_writable_with_chown "/data/transactions"
104 | fi
105 | fi
106 | if [ -d /backups ]; then
107 | check_mounted_folder_writable_with_chown "/backups"
108 | fi
109 |
110 | # ==== MAKE SURE NEO4J CANNOT BE RUN FROM THIS CONTAINER ====
111 |
112 | rm ${NEO4J_HOME}/bin/neo4j
113 |
114 | if [[ "${1}" == "neo4j" ]]; then
115 | correct_image="neo4j:"$(neo4j-admin --version)"-${NEO4J_EDITION}"
116 | echo >&2 "
117 | This is a neo4j-admin only image, and usage of Neo4j server is not supported from here.
118 | If you wish to start a Neo4j database, use:
119 |
120 | docker run ${correct_image}
121 | "
122 | exit 1
123 | fi
124 |
125 | # ==== START NEO4J-ADMIN COMMAND ====
126 |
127 | ${exec_cmd} "${@}"
128 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/docker-image-src/4.2/neo4j-admin/docker-entrypoint.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash -eu
2 |
3 | function running_as_root
4 | {
5 | test "$(id -u)" = "0"
6 | }
7 |
8 | function is_writable
9 | {
10 | gosu "${userid}":"${groupid}" test -w "${1}"
11 | }
12 |
13 | function print_permissions_advice_and_fail
14 | {
15 | local _directory=${1}
16 | echo >&2 "
17 | Folder ${_directory} is not accessible for user: ${userid} or group ${groupid} or groups ${groups[@]}, this is commonly a file permissions issue on the mounted folder.
18 |
19 | Hints to solve the issue:
20 | 1) Make sure the folder exists before mounting it. Docker will create the folder using root permissions before starting the Neo4j container. The root permissions disallow Neo4j from writing to the mounted folder.
21 | 2) Pass the folder owner's user ID and group ID to docker run, so that docker runs as that user.
22 | If the folder is owned by the current user, this can be done by adding this flag to your docker run command:
23 | --user=\$(id -u):\$(id -g)
24 | "
25 | exit 1
26 | }
27 |
28 |
29 | function check_mounted_folder_writable_with_chown
30 | {
31 | local mountFolder=${1}
32 | if running_as_root; then
33 | # check folder permissions
34 | if ! is_writable "${mountFolder}" ; then
35 | # warn that we're about to chown the folder and then chown it
36 | echo "Warning: Folder mounted to \"${mountFolder}\" is not writable from inside container. Changing folder owner to ${userid}."
37 | chown -R "${userid}":"${groupid}" "${mountFolder}"
38 | # check permissions on files in the folder
39 | elif [ $(gosu "${userid}":"${groupid}" find "${mountFolder}" -not -writable | wc -l) -gt 0 ]; then
40 | echo "Warning: Some files inside \"${mountFolder}\" are not writable from inside container. Changing folder owner to ${userid}."
41 | chown -R "${userid}":"${groupid}" "${mountFolder}"
42 | fi
43 | else
44 | if [[ ! -w "${mountFolder}" ]] && [[ "$(stat -c %U ${mountFolder})" != "neo4j" ]]; then
45 | print_permissions_advice_and_fail "${mountFolder}"
46 | fi
47 | fi
48 | }
49 |
50 | # ==== SETUP WHICH USER TO RUN AS ====
51 |
52 | if running_as_root; then
53 | userid="neo4j"
54 | groupid="neo4j"
55 | groups=($(id -G neo4j))
56 | exec_cmd="exec gosu neo4j:neo4j"
57 | else
58 | userid="$(id -u)"
59 | groupid="$(id -g)"
60 | groups=($(id -G))
61 | exec_cmd="exec"
62 | fi
63 |
64 |
65 | # ==== CHECK LICENSE AGREEMENT ====
66 |
67 | if [ "${NEO4J_EDITION}" == "enterprise" ]; then
68 | if [ "${NEO4J_ACCEPT_LICENSE_AGREEMENT:=no}" != "yes" ]; then
69 | echo >&2 "
70 | In order to use Neo4j Enterprise Edition you must accept the license agreement.
71 |
72 | (c) Neo4j Sweden AB. 2021. All Rights Reserved.
73 | Use of this Software without a proper commercial license with Neo4j,
74 | Inc. or its affiliates is prohibited.
75 |
76 | Email inquiries can be directed to: [email protected]
77 |
78 | More information is also available at: https://neo4j.com/licensing/
79 |
80 |
81 | To accept the license agreement set the environment variable
82 | NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
83 |
84 | To do this you can use the following docker argument:
85 |
86 | --env=NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
87 | "
88 | exit 1
89 | fi
90 | fi
91 |
92 | # ==== ENSURE MOUNT FOLDER READ/WRITABILITY ====
93 |
94 | if [ -d /data ]; then
95 | check_mounted_folder_writable_with_chown "/data"
96 | if [ -d /data/databases ]; then
97 | check_mounted_folder_writable_with_chown "/data/databases"
98 | fi
99 | if [ -d /data/dbms ]; then
100 | check_mounted_folder_writable_with_chown "/data/dbms"
101 | fi
102 | if [ -d /data/transactions ]; then
103 | check_mounted_folder_writable_with_chown "/data/transactions"
104 | fi
105 | fi
106 | if [ -d /backups ]; then
107 | check_mounted_folder_writable_with_chown "/backups"
108 | fi
109 |
110 | # ==== MAKE SURE NEO4J CANNOT BE RUN FROM THIS CONTAINER ====
111 |
112 | rm ${NEO4J_HOME}/bin/neo4j
113 |
114 | if [[ "${1}" == "neo4j" ]]; then
115 | correct_image="neo4j:"$(neo4j-admin --version)"-${NEO4J_EDITION}"
116 | echo >&2 "
117 | This is a neo4j-admin only image, and usage of Neo4j server is not supported from here.
118 | If you wish to start a Neo4j database, use:
119 |
120 | docker run ${correct_image}
121 | "
122 | exit 1
123 | fi
124 |
125 | # ==== START NEO4J-ADMIN COMMAND ====
126 |
127 | ${exec_cmd} "${@}"
128 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/publish-neo4j-admin-image.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash
2 | set -eu -o pipefail
3 |
4 | EDITIONS=("community" "enterprise")
5 |
6 | ROOT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
7 | source "$ROOT_DIR/build-utils-common-functions.sh"
8 | BUILD_DIR=${ROOT_DIR}/build
9 | SRC_DIR=${ROOT_DIR}/docker-image-src
10 | # shellcheck disable=SC2034 # Used in docker-common-functions.sh
11 | TAR_CACHE=${ROOT_DIR}/in
12 |
13 | function usage
14 | {
15 | echo >&2 "USAGE: $0 <version> <edition> <operating system> <repository>
16 | For example:
17 | $0 4.4.10 community debian neo/neo4j-admin
18 | $0 5.10.0 enterprise ubi9 neo/neo4j-admin
19 | Version and operating system can also be set in the environment.
20 | For example:
21 | NEO4JVERSION=4.4.10 NEO4JEDITION=community IMAGE_OS=debian REPOSITORY=neo/neo4j-admin $0
22 | NEO4JVERSION=5.10.0 NEO4JEDITION=enterprise IMAGE_OS=ubi9 REPOSITORY=neo/neo4j-admin $0
23 | "
24 | exit 1
25 | }
26 |
27 | ## ==========================================
28 | ## get and sanitise script inputs
29 |
30 | if [[ $# -eq 4 ]]; then
31 | NEO4JVERSION=${1}
32 | NEO4JEDITION=${2}
33 | IMAGE_OS=${3}
34 | REPOSITORY=${4}
35 | elif [[ -z ${NEO4JVERSION:-""} ]]; then
36 | echo >&2 "NEO4JVERSION is unset. Either set it in the environment or pass as argument to this script."
37 | usage
38 | elif [[ -z ${NEO4JEDITION:-""} ]]; then
39 | echo >&2 "NEO4JEDITION is unset. Either set it in the environment or pass as argument to this script."
40 | usage
41 | elif [[ -z ${IMAGE_OS:-""} ]]; then
42 | echo >&2 "IMAGE_OS is unset. Either set it in the environment or pass as argument to this script."
43 | usage
44 | elif [[ -z ${REPOSITORY:-""} ]]; then
45 | echo >&2 "REPOSITORY is unset. Either set it in the environment or pass as argument to this script."
46 | usage
47 | fi
48 | # verify edition
49 | if ! contains_element "${NEO4JEDITION}" "${EDITIONS[@]}"; then
50 | echo >&2 "${NEO4JEDITION} is not a supported edition."
51 | usage
52 | fi
53 | # verify compatible neo4j version
54 | if [[ ! "${NEO4JVERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then
55 | echo "\"${NEO4JVERSION}\" is not a valid version number."
56 | usage
57 | fi
58 | # get source files
59 | BRANCH=$(get_branch_from_version ${NEO4JVERSION})
60 | DOCKERFILE_NAME=$(get_compatible_dockerfile_for_os_or_error "${BRANCH}" "${IMAGE_OS}")
61 |
62 | echo "Building local context for docker build"
63 | ADMIN_LOCALCXT_DIR=${BUILD_DIR}/${IMAGE_OS}/neo4j-admin/${NEO4JEDITION}
64 | mkdir -p ${ADMIN_LOCALCXT_DIR}
65 |
66 | # copy neo4j-admin sources
67 | mkdir -p ${ADMIN_LOCALCXT_DIR}/local-package
68 | cp ${SRC_DIR}/common/* ${ADMIN_LOCALCXT_DIR}/local-package
69 | cp "$(cached_tarball "${NEO4JVERSION}" "${NEO4JEDITION}")" ${ADMIN_LOCALCXT_DIR}/local-package/
70 | cp ${SRC_DIR}/${BRANCH}/neo4j-admin/*.sh ${ADMIN_LOCALCXT_DIR}/local-package
71 |
72 | # create neo4j-admin Dockerfile
73 | cp "${SRC_DIR}/${BRANCH}/neo4j-admin/${DOCKERFILE_NAME}" "${ADMIN_LOCALCXT_DIR}/Dockerfile"
74 | coredb_sha=$(shasum --algorithm=256 "$(cached_tarball "${NEO4JVERSION}" "${NEO4JEDITION}")" | cut -d' ' -f1)
75 | sed -i -e "s|%%NEO4J_SHA%%|${coredb_sha}|" "${ADMIN_LOCALCXT_DIR}/Dockerfile"
76 | sed -i -e "s|%%NEO4J_TARBALL%%|$(tarball_name ${NEO4JVERSION} ${NEO4JEDITION})|" "${ADMIN_LOCALCXT_DIR}/Dockerfile"
77 | sed -i -e "s|%%NEO4J_EDITION%%|${NEO4JEDITION}|" "${ADMIN_LOCALCXT_DIR}/Dockerfile"
78 |
79 | # build and push neo4j-admin
80 | MAJOR=$(get_major_from_version "${NEO4JVERSION}")
81 | full_version_admin_image_tag="${REPOSITORY}:${NEO4JVERSION}-${NEO4JEDITION}-${IMAGE_OS}"
82 | major_minor_admin_image_tag="${REPOSITORY}:${NEO4JVERSION%.*}-${NEO4JEDITION}-${IMAGE_OS}"
83 | major_admin_image_tag="${REPOSITORY}:${MAJOR}-${NEO4JEDITION}-${IMAGE_OS}"
84 | echo "Building neo4j-admin docker image for neo4j-admin-${NEO4JVERSION} ${NEO4JEDITION} on ${IMAGE_OS}."
85 | echo "With tags: ${full_version_admin_image_tag},${major_minor_admin_image_tag},${major_admin_image_tag}"
86 |
87 | docker buildx build --tag="${full_version_admin_image_tag}" --tag="${major_minor_admin_image_tag}" --tag="${major_admin_image_tag}" \
88 | --build-arg="NEO4J_URI=file:///startup/$(tarball_name "${NEO4JVERSION}" "${NEO4JEDITION}")" \
89 | "${ADMIN_LOCALCXT_DIR}" --platform linux/amd64,linux/arm64 --push
90 |
91 |
92 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/pom.xml:
--------------------------------------------------------------------------------
```
1 | <?xml version="1.0" encoding="UTF-8"?>
2 | <project xmlns="http://maven.apache.org/POM/4.0.0"
3 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5 | <modelVersion>4.0.0</modelVersion>
6 |
7 | <groupId>com.neo4j</groupId>
8 | <artifactId>docker-neo4j-tests</artifactId>
9 | <version>1.0-SNAPSHOT</version>
10 | <packaging>jar</packaging>
11 |
12 | <properties>
13 | <java.version>17</java.version>
14 | <junit.version>5.11.0</junit.version>
15 | <neo4j.driver.version>5.24.0</neo4j.driver.version>
16 | <surefire.version>3.1.2</surefire.version>
17 | <testcontainers.version>1.20.1</testcontainers.version>
18 | </properties>
19 |
20 | <profiles>
21 | <profile>
22 | <!-- This sets what test groups will run by default if you don't specify any maven profiles when running mvn test -->
23 | <id>default</id>
24 | <activation>
25 | <activeByDefault>true</activeByDefault>
26 | </activation>
27 | <properties>
28 | <profile.include></profile.include>
29 | <profile.exclude>BundleTest</profile.exclude>
30 | </properties>
31 | </profile>
32 | <profile>
33 | <id>bundle</id>
34 | <properties>
35 | <profile.include>BundleTest</profile.include>
36 | </properties>
37 | </profile>
38 | </profiles>
39 |
40 | <build>
41 | <plugins>
42 | <plugin>
43 | <groupId>org.apache.maven.plugins</groupId>
44 | <artifactId>maven-compiler-plugin</artifactId>
45 | <version>3.7.0</version>
46 | <configuration>
47 | <source>${java.version}</source>
48 | <target>${java.version}</target>
49 | </configuration>
50 | </plugin>
51 | <plugin>
52 | <groupId>org.apache.maven.plugins</groupId>
53 | <artifactId>maven-surefire-plugin</artifactId>
54 | <version>${surefire.version}</version>
55 | <configuration>
56 | <groups>${profile.include}</groups>
57 | <excludedGroups>${profile.exclude}</excludedGroups>
58 | </configuration>
59 | </plugin>
60 | </plugins>
61 | </build>
62 |
63 | <dependencies>
64 | <dependency>
65 | <!-- logging library -->
66 | <groupId>org.slf4j</groupId>
67 | <artifactId>slf4j-api</artifactId>
68 | <version>1.7.32</version>
69 | </dependency>
70 | <dependency>
71 | <groupId>org.slf4j</groupId>
72 | <artifactId>slf4j-log4j12</artifactId>
73 | <version>1.7.32</version>
74 | </dependency>
75 | <dependency>
76 | <groupId>org.junit.jupiter</groupId>
77 | <artifactId>junit-jupiter-api</artifactId>
78 | <version>${junit.version}</version>
79 | <scope>test</scope>
80 | </dependency>
81 | <dependency>
82 | <groupId>org.junit.jupiter</groupId>
83 | <artifactId>junit-jupiter-engine</artifactId>
84 | <version>${junit.version}</version>
85 | <scope>test</scope>
86 | </dependency>
87 | <dependency>
88 | <groupId>org.junit.jupiter</groupId>
89 | <artifactId>junit-jupiter-params</artifactId>
90 | <version>${junit.version}</version>
91 | <scope>test</scope>
92 | </dependency>
93 | <dependency>
94 | <groupId>org.testcontainers</groupId>
95 | <artifactId>junit-jupiter</artifactId>
96 | <version>${testcontainers.version}</version>
97 | <scope>test</scope>
98 | </dependency>
99 | <dependency>
100 | <groupId>org.testcontainers</groupId>
101 | <artifactId>testcontainers</artifactId>
102 | <version>${testcontainers.version}</version>
103 | <scope>test</scope>
104 | </dependency>
105 | <dependency>
106 | <groupId>org.neo4j.driver</groupId>
107 | <artifactId>neo4j-java-driver</artifactId>
108 | <version>${neo4j.driver.version}</version>
109 | <scope>test</scope>
110 | </dependency>
111 | <!-- Gson: Java to Json conversion -->
112 | <dependency>
113 | <groupId>com.google.code.gson</groupId>
114 | <artifactId>gson</artifactId>
115 | <version>2.8.9</version>
116 | <scope>test</scope>
117 | </dependency>
118 | <!-- For creating archives of test data -->
119 | <dependency>
120 | <groupId>org.apache.commons</groupId>
121 | <artifactId>commons-compress</artifactId>
122 | <version>1.24.0</version>
123 | <scope>test</scope>
124 | </dependency>
125 | <dependency>
126 | <groupId>commons-io</groupId>
127 | <artifactId>commons-io</artifactId>
128 | <version>2.11.0</version>
129 | <scope>test</scope>
130 | </dependency>
131 | </dependencies>
132 |
133 | </project>
134 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/docker-image-src/4.4/neo4j-admin/docker-entrypoint.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash -eu
2 |
3 | # load useful utility functions
4 | . /startup/utilities.sh
5 |
6 | function check_mounted_folder_writable_with_chown
7 | {
8 | local mountFolder=${1}
9 | debug_msg "checking ${mountFolder} is writable"
10 | if running_as_root; then
11 | # check folder permissions
12 | if ! is_writable "${mountFolder}" ; then
13 | # warn that we're about to chown the folder and then chown it
14 | echo "Warning: Folder mounted to \"${mountFolder}\" is not writable from inside container. Changing folder owner to ${userid}."
15 | chown -R "${userid}":"${groupid}" "${mountFolder}"
16 | # check permissions on files in the folder
17 | elif [ $(${exec_cmd} find "${mountFolder}" -not -writable | wc -l) -gt 0 ]; then
18 | echo "Warning: Some files inside \"${mountFolder}\" are not writable from inside container. Changing folder owner to ${userid}."
19 | chown -R "${userid}":"${groupid}" "${mountFolder}"
20 | fi
21 | else
22 | if [[ ! -w "${mountFolder}" ]] && [[ "$(stat -c %U ${mountFolder})" != "neo4j" ]]; then
23 | print_permissions_advice_and_fail "${mountFolder}"
24 | fi
25 | fi
26 | }
27 |
28 | # ==== SETUP WHICH USER TO RUN AS ====
29 | debug_msg "DEBUGGING ENABLED"
30 |
31 | if running_as_root; then
32 | userid="neo4j"
33 | groupid="neo4j"
34 | groups=($(id -G neo4j))
35 | exec_cmd="runuser -p -u neo4j -g neo4j --"
36 | debug_msg "Running as root user inside neo4j-admin image"
37 | else
38 | userid="$(id -u)"
39 | groupid="$(id -g)"
40 | groups=($(id -G))
41 | exec_cmd="exec"
42 | debug_msg "Running as user ${userid} inside neo4j-admin image"
43 | fi
44 |
45 | # ==== MAKE SURE NEO4J CANNOT BE RUN FROM THIS CONTAINER ====
46 | debug_msg "checking neo4j was not requested"
47 | if [[ "${1}" == "neo4j" ]]; then
48 | correct_image="neo4j:"$(neo4j-admin --version)"-${NEO4J_EDITION}"
49 | echo >&2 "
50 | This is a neo4j-admin only image, and usage of Neo4j server is not supported from here.
51 | If you wish to start a Neo4j database, use:
52 |
53 | docker run ${correct_image}
54 | "
55 | exit 1
56 | fi
57 |
58 | # ==== MAKE SURE NEO4J-ADMIN REPORT CANNOT BE RUN FROM THIS CONTAINER ====
59 | debug_msg "checking neo4j-admin report was not requested"
60 | # maybe make sure the command is neo4j-admin server report rather than just anything mentioning reports?
61 | if containsElement "report" "${@}"; then
62 | echo >&2 \
63 | "neo4j-admin report must be run in the same container as neo4j
64 | otherwise the report tool cannot access relevant files and processes required for generating the report.
65 |
66 | To run the report tool inside a neo4j container, do:
67 |
68 | docker exec <CONTAINER NAME> neo4j-admin-report
69 |
70 | "
71 | exit 1
72 | fi
73 |
74 |
75 | # ==== CHECK LICENSE AGREEMENT ====
76 |
77 | debug_msg "checking license"
78 | if [ "${NEO4J_EDITION}" == "enterprise" ]; then
79 | if [ "${NEO4J_ACCEPT_LICENSE_AGREEMENT:=no}" != "yes" ]; then
80 | echo >&2 "
81 | In order to use Neo4j Enterprise Edition you must accept the license agreement.
82 |
83 | The license agreement is available at https://neo4j.com/terms/licensing/
84 | If you have a support contract the following terms apply https://neo4j.com/terms/support-terms/
85 |
86 | (c) Neo4j Sweden AB. All Rights Reserved.
87 | Use of this Software without a proper commercial license
88 | with Neo4j, Inc. or its affiliates is prohibited.
89 | Neo4j has the right to terminate your usage if you are not compliant.
90 |
91 | More information is also available at: https://neo4j.com/licensing/
92 | If you have further inquiries about licensing, please contact us via https://neo4j.com/contact-us/
93 |
94 | To accept the commercial license agreement set the environment variable
95 | NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
96 |
97 | To do this you can use the following docker argument:
98 |
99 | --env=NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
100 | "
101 | exit 1
102 | fi
103 | fi
104 |
105 | # ==== ENSURE MOUNT FOLDER READ/WRITABILITY ====
106 | debug_msg "Checking for mounted folder writability"
107 |
108 | if [ -d /data ]; then
109 | check_mounted_folder_writable_with_chown "/data"
110 | if [ -d /data/databases ]; then
111 | check_mounted_folder_writable_with_chown "/data/databases"
112 | fi
113 | if [ -d /data/dbms ]; then
114 | check_mounted_folder_writable_with_chown "/data/dbms"
115 | fi
116 | if [ -d /data/transactions ]; then
117 | check_mounted_folder_writable_with_chown "/data/transactions"
118 | fi
119 | fi
120 | if [ -d /backups ]; then
121 | check_mounted_folder_writable_with_chown "/backups"
122 | fi
123 |
124 | # ==== START NEO4J-ADMIN COMMAND ====
125 | if debugging_enabled; then
126 | echo ${exec_cmd} "${@}" --verbose
127 | ${exec_cmd} "${@}" --verbose
128 | else
129 | ${exec_cmd} "${@}"
130 | fi
131 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/src/test/java/com/neo4j/docker/coredb/TestCausalCluster.java:
--------------------------------------------------------------------------------
```java
1 | package com.neo4j.docker.coredb;
2 |
3 | import com.neo4j.docker.utils.SetContainerUser;
4 | import com.neo4j.docker.utils.TemporaryFolderManager;
5 | import com.neo4j.docker.utils.TestSettings;
6 | import org.junit.jupiter.api.Assertions;
7 | import org.junit.jupiter.api.Assumptions;
8 | import org.junit.jupiter.api.Disabled;
9 | import org.junit.jupiter.api.Test;
10 | import org.neo4j.driver.*;
11 |
12 | import org.junit.jupiter.api.extension.RegisterExtension;
13 | import org.testcontainers.containers.DockerComposeContainer;
14 | import org.testcontainers.containers.wait.strategy.*;
15 |
16 | import java.io.DataOutputStream;
17 | import java.io.File;
18 | import java.io.FileOutputStream;
19 | import java.io.InputStream;
20 | import java.nio.file.Files;
21 | import java.nio.file.Path;
22 | import java.nio.file.Paths;
23 | import java.time.Duration;
24 |
25 | @Disabled
26 | public class TestCausalCluster
27 | {
28 | private static final int DEFAULT_BOLT_PORT = 7687;
29 | @RegisterExtension
30 | public static TemporaryFolderManager temporaryFolderManager = new TemporaryFolderManager();
31 |
32 | @Disabled
33 | @Test
34 | void testCausalClusteringBasic() throws Exception
35 | {
36 | Assumptions.assumeTrue(TestSettings.EDITION == TestSettings.Edition.ENTERPRISE,
37 | "No causal clustering for community edition");
38 |
39 | Path tmpDir = temporaryFolderManager.createFolder( "CC_cluster_" );
40 |
41 | File compose_file = new File(tmpDir.toString(), "causal-cluster-compose.yml");
42 | Files.copy(getResource("causal-cluster-compose.yml"), Paths.get(compose_file.getPath()));
43 |
44 | Files.createDirectories( tmpDir.resolve( "core1" ) );
45 | Files.createDirectories( tmpDir.resolve( "core2" ) );
46 | Files.createDirectories( tmpDir.resolve( "core3" ) );
47 | Files.createDirectories( tmpDir.resolve( "readreplica1" ) );
48 |
49 | String content = new String(Files.readAllBytes(Paths.get(compose_file.getPath())));
50 | String[] contentLines = content.split(System.getProperty("line.separator"));
51 | String[] editedLines = new String[contentLines.length];
52 | int i = 0;
53 |
54 | for (String line : contentLines) {
55 | editedLines[i] = line.replaceAll("%%IMAGE%%", TestSettings.IMAGE_ID.asCanonicalNameString());
56 | editedLines[i] = editedLines[i].replaceAll("%%LOGS_DIR%%", tmpDir.toAbsolutePath().toString());
57 | editedLines[i] = editedLines[i].replaceAll("%%USERIDGROUPID%%", SetContainerUser.getNonRootUserString());
58 | i++;
59 | }
60 |
61 | String editedContent = String.join("\n", editedLines);
62 |
63 | DataOutputStream outstream = new DataOutputStream(new FileOutputStream(compose_file,false));
64 | outstream.write(editedContent.getBytes());
65 | outstream.close();
66 | System.out.println("logs: " + compose_file.getName() + " and " + tmpDir.toString());
67 |
68 | DockerComposeContainer clusteringContainer = new DockerComposeContainer(compose_file)
69 | .withLocalCompose(true)
70 | .withExposedService("core1", DEFAULT_BOLT_PORT )
71 | .withExposedService("core1", 7474,
72 | Wait.forHttp( "/" )
73 | .forPort( 7474 )
74 | .forStatusCode( 200 )
75 | .withStartupTimeout( Duration.ofSeconds( 300 ) ))
76 | .withExposedService("readreplica1", DEFAULT_BOLT_PORT);
77 |
78 | clusteringContainer.start();
79 |
80 | String core1Uri = "bolt://" + clusteringContainer.getServiceHost("core1", DEFAULT_BOLT_PORT)
81 | + ":" +
82 | clusteringContainer.getServicePort("core1", DEFAULT_BOLT_PORT);
83 | String rrUri = "bolt://" + clusteringContainer.getServiceHost("readreplica1", DEFAULT_BOLT_PORT)
84 | + ":" +
85 | clusteringContainer.getServicePort("readreplica1", DEFAULT_BOLT_PORT);
86 |
87 | try ( Driver coreDriver = GraphDatabase.driver( core1Uri, AuthTokens.basic( "neo4j", "neo")))
88 | {
89 | Session session = coreDriver.session();
90 | Result rs = session.run( "CREATE (arne:dog {name:'Arne'})-[:SNIFFS]->(bosse:dog {name:'Bosse'}) RETURN arne.name");
91 | Assertions.assertEquals( "Arne", rs.single().get( 0 ).asString(), "did not receive expected result from cypher CREATE query" );
92 | }
93 | catch (Exception e)
94 | {
95 | clusteringContainer.stop();
96 | return;
97 | }
98 |
99 | try ( Driver rrDriver = GraphDatabase.driver(rrUri, AuthTokens.basic("neo4j", "neo")))
100 | {
101 | Session session = rrDriver.session();
102 | Result rs = session.run( "MATCH (a:dog)-[:SNIFFS]->(b:dog) RETURN a.name");
103 | Assertions.assertEquals( "Arne", rs.single().get( 0 ).asString(), "did not receive expected result from cypher MATCH query" );
104 | }
105 | catch (Exception e)
106 | {
107 | clusteringContainer.stop();
108 | return;
109 | }
110 |
111 | clusteringContainer.stop();
112 |
113 | }
114 |
115 | private InputStream getResource(String path) {
116 | InputStream resource = getClass().getClassLoader().getResourceAsStream(path);
117 | return resource;
118 | }
119 | }
120 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/docker-image-src/5/neo4j-admin/docker-entrypoint.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash -eu
2 |
3 | # load useful utility functions
4 | . /startup/utilities.sh
5 |
6 | function check_mounted_folder_writable_with_chown
7 | {
8 | local mountFolder=${1}
9 | debug_msg "checking ${mountFolder} is writable"
10 | if running_as_root; then
11 | # check folder permissions
12 | if ! is_writable "${mountFolder}" ; then
13 | # warn that we're about to chown the folder and then chown it
14 | echo "Warning: Folder mounted to \"${mountFolder}\" is not writable from inside container. Changing folder owner to ${userid}."
15 | chown -R "${userid}":"${groupid}" "${mountFolder}"
16 | # check permissions on files in the folder
17 | elif [ $(${exec_cmd} find "${mountFolder}" -not -writable | wc -l) -gt 0 ]; then
18 | echo "Warning: Some files inside \"${mountFolder}\" are not writable from inside container. Changing folder owner to ${userid}."
19 | chown -R "${userid}":"${groupid}" "${mountFolder}"
20 | fi
21 | else
22 | if [[ ! -w "${mountFolder}" ]] && [[ "$(stat -c %U ${mountFolder})" != "neo4j" ]]; then
23 | print_permissions_advice_and_fail "${mountFolder}" "${userid}" "${groupid}"
24 | fi
25 | fi
26 | }
27 |
28 | # ==== SETUP WHICH USER TO RUN AS ====
29 | debug_msg "DEBUGGING ENABLED"
30 |
31 | if running_as_root; then
32 | userid="neo4j"
33 | groupid="neo4j"
34 | groups=($(id -G neo4j))
35 | exec_cmd="runuser -p -u neo4j -g neo4j --"
36 | debug_msg "Running as root user inside neo4j-admin image"
37 | else
38 | userid="$(id -u)"
39 | groupid="$(id -g)"
40 | groups=($(id -G))
41 | exec_cmd="exec"
42 | debug_msg "Running as user ${userid} inside neo4j-admin image"
43 | fi
44 |
45 | #%%DEPRECATION_WARNING_PLACEHOLDER%%
46 |
47 | # ==== MAKE SURE NEO4J CANNOT BE RUN FROM THIS CONTAINER ====
48 | debug_msg "checking neo4j was not requested"
49 | if [[ "${1}" == "neo4j" ]]; then
50 | correct_image="neo4j:"$(neo4j-admin --version)"-${NEO4J_EDITION}"
51 | echo >&2 "
52 | This is a neo4j-admin only image, and usage of Neo4j server is not supported from here.
53 | If you wish to start a Neo4j database, use:
54 |
55 | docker run ${correct_image}
56 | "
57 | exit 1
58 | fi
59 |
60 | # ==== MAKE SURE NEO4J-ADMIN REPORT CANNOT BE RUN FROM THIS CONTAINER ====
61 | debug_msg "checking neo4j-admin report was not requested"
62 | # maybe make sure the command is neo4j-admin server report rather than just anything mentioning reports?
63 | if containsElement "report" "${@}"; then
64 | echo >&2 \
65 | "neo4j-admin report must be run in the same container as neo4j
66 | otherwise the report tool cannot access relevant files and processes required for generating the report.
67 |
68 | To run the report tool inside a neo4j container, do:
69 |
70 | docker exec <CONTAINER NAME> neo4j-admin-report
71 |
72 | "
73 | exit 1
74 | fi
75 |
76 |
77 | # ==== CHECK LICENSE AGREEMENT ====
78 |
79 | debug_msg "checking license"
80 | # Only prompt for license agreement if command contains "neo4j" in it
81 | if [[ "${1}" == *"neo4j"* ]]; then
82 | if [ "${NEO4J_EDITION}" == "enterprise" ]; then
83 | : ${NEO4J_ACCEPT_LICENSE_AGREEMENT:="not accepted"}
84 | if [[ "$NEO4J_ACCEPT_LICENSE_AGREEMENT" != "yes" && "$NEO4J_ACCEPT_LICENSE_AGREEMENT" != "eval" ]]; then
85 | echo >&2 "
86 | In order to use Neo4j Enterprise Edition you must accept the license agreement.
87 |
88 | The license agreement is available at https://neo4j.com/terms/licensing/
89 | If you have a support contract the following terms apply https://neo4j.com/terms/support-terms/
90 |
91 | If you do not have a commercial license and want to evaluate the Software
92 | please read the terms of the evaluation agreement before you accept.
93 | https://neo4j.com/terms/enterprise_us/
94 |
95 | (c) Neo4j Sweden AB. All Rights Reserved.
96 | Use of this Software without a proper commercial license, or evaluation license
97 | with Neo4j, Inc. or its affiliates is prohibited.
98 | Neo4j has the right to terminate your usage if you are not compliant.
99 |
100 | More information is also available at: https://neo4j.com/licensing/
101 | If you have further inquiries about licensing, please contact us via https://neo4j.com/contact-us/
102 |
103 | To accept the commercial license agreement set the environment variable
104 | NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
105 |
106 | To accept the terms of the evaluation agreement set the environment variable
107 | NEO4J_ACCEPT_LICENSE_AGREEMENT=eval
108 |
109 | To do this you can use the following docker argument:
110 |
111 | --env=NEO4J_ACCEPT_LICENSE_AGREEMENT=<yes|eval>
112 | "
113 | exit 1
114 | fi
115 | fi
116 | fi
117 |
118 | # ==== ENSURE MOUNT FOLDER READ/WRITABILITY ====
119 | debug_msg "Checking for mounted folder writability"
120 |
121 | if [ -d /data ]; then
122 | check_mounted_folder_writable_with_chown "/data"
123 | if [ -d /data/databases ]; then
124 | check_mounted_folder_writable_with_chown "/data/databases"
125 | fi
126 | if [ -d /data/dbms ]; then
127 | check_mounted_folder_writable_with_chown "/data/dbms"
128 | fi
129 | if [ -d /data/transactions ]; then
130 | check_mounted_folder_writable_with_chown "/data/transactions"
131 | fi
132 | fi
133 | if [ -d /backups ]; then
134 | check_mounted_folder_writable_with_chown "/backups"
135 | fi
136 |
137 | # ==== START NEO4J-ADMIN COMMAND ====
138 | if debugging_enabled; then
139 | echo ${exec_cmd} "${@}" --verbose
140 | ${exec_cmd} "${@}" --verbose
141 | else
142 | ${exec_cmd} "${@}"
143 | fi
144 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/docker-image-src/calver/neo4j-admin/docker-entrypoint.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash -eu
2 |
3 | # load useful utility functions
4 | . /startup/utilities.sh
5 |
6 | function check_mounted_folder_writable_with_chown
7 | {
8 | local mountFolder=${1}
9 | debug_msg "checking ${mountFolder} is writable"
10 | if running_as_root; then
11 | # check folder permissions
12 | if ! is_writable "${mountFolder}" ; then
13 | # warn that we're about to chown the folder and then chown it
14 | echo "Warning: Folder mounted to \"${mountFolder}\" is not writable from inside container. Changing folder owner to ${userid}."
15 | chown -R "${userid}":"${groupid}" "${mountFolder}"
16 | # check permissions on files in the folder
17 | elif [ $(${exec_cmd} find "${mountFolder}" -not -writable | wc -l) -gt 0 ]; then
18 | echo "Warning: Some files inside \"${mountFolder}\" are not writable from inside container. Changing folder owner to ${userid}."
19 | chown -R "${userid}":"${groupid}" "${mountFolder}"
20 | fi
21 | else
22 | if [[ ! -w "${mountFolder}" ]] && [[ "$(stat -c %U ${mountFolder})" != "neo4j" ]]; then
23 | print_permissions_advice_and_fail "${mountFolder}" "${userid}" "${groupid}"
24 | fi
25 | fi
26 | }
27 |
28 | # ==== SETUP WHICH USER TO RUN AS ====
29 | debug_msg "DEBUGGING ENABLED"
30 |
31 | if running_as_root; then
32 | userid="neo4j"
33 | groupid="neo4j"
34 | groups=($(id -G neo4j))
35 | exec_cmd="runuser -p -u neo4j -g neo4j --"
36 | debug_msg "Running as root user inside neo4j-admin image"
37 | else
38 | userid="$(id -u)"
39 | groupid="$(id -g)"
40 | groups=($(id -G))
41 | exec_cmd="exec"
42 | debug_msg "Running as user ${userid} inside neo4j-admin image"
43 | fi
44 |
45 | #%%DEPRECATION_WARNING_PLACEHOLDER%%
46 |
47 | # ==== MAKE SURE NEO4J CANNOT BE RUN FROM THIS CONTAINER ====
48 | debug_msg "checking neo4j was not requested"
49 | if [[ "${1}" == "neo4j" ]]; then
50 | correct_image="neo4j:"$(neo4j-admin --version)"-${NEO4J_EDITION}"
51 | echo >&2 "
52 | This is a neo4j-admin only image, and usage of Neo4j server is not supported from here.
53 | If you wish to start a Neo4j database, use:
54 |
55 | docker run ${correct_image}
56 | "
57 | exit 1
58 | fi
59 |
60 | # ==== MAKE SURE NEO4J-ADMIN REPORT CANNOT BE RUN FROM THIS CONTAINER ====
61 | debug_msg "checking neo4j-admin report was not requested"
62 | # maybe make sure the command is neo4j-admin server report rather than just anything mentioning reports?
63 | if containsElement "report" "${@}"; then
64 | echo >&2 \
65 | "neo4j-admin report must be run in the same container as neo4j
66 | otherwise the report tool cannot access relevant files and processes required for generating the report.
67 |
68 | To run the report tool inside a neo4j container, do:
69 |
70 | docker exec <CONTAINER NAME> neo4j-admin-report
71 |
72 | "
73 | exit 1
74 | fi
75 |
76 |
77 | # ==== CHECK LICENSE AGREEMENT ====
78 |
79 | debug_msg "checking license"
80 | # Only prompt for license agreement if command contains "neo4j" in it
81 | if [[ "${1}" == *"neo4j"* ]]; then
82 | if [ "${NEO4J_EDITION}" == "enterprise" ]; then
83 | : ${NEO4J_ACCEPT_LICENSE_AGREEMENT:="not accepted"}
84 | if [[ "$NEO4J_ACCEPT_LICENSE_AGREEMENT" != "yes" && "$NEO4J_ACCEPT_LICENSE_AGREEMENT" != "eval" ]]; then
85 | echo >&2 "
86 | In order to use Neo4j Enterprise Edition you must accept the license agreement.
87 |
88 | The license agreement is available at https://neo4j.com/terms/licensing/
89 | If you have a support contract the following terms apply https://neo4j.com/terms/support-terms/
90 |
91 | If you do not have a commercial license and want to evaluate the Software
92 | please read the terms of the evaluation agreement before you accept.
93 | https://neo4j.com/terms/enterprise_us/
94 |
95 | (c) Neo4j Sweden AB. All Rights Reserved.
96 | Use of this Software without a proper commercial license, or evaluation license
97 | with Neo4j, Inc. or its affiliates is prohibited.
98 | Neo4j has the right to terminate your usage if you are not compliant.
99 |
100 | More information is also available at: https://neo4j.com/licensing/
101 | If you have further inquiries about licensing, please contact us via https://neo4j.com/contact-us/
102 |
103 | To accept the commercial license agreement set the environment variable
104 | NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
105 |
106 | To accept the terms of the evaluation agreement set the environment variable
107 | NEO4J_ACCEPT_LICENSE_AGREEMENT=eval
108 |
109 | To do this you can use the following docker argument:
110 |
111 | --env=NEO4J_ACCEPT_LICENSE_AGREEMENT=<yes|eval>
112 | "
113 | exit 1
114 | fi
115 | fi
116 | fi
117 |
118 | # ==== ENSURE MOUNT FOLDER READ/WRITABILITY ====
119 | debug_msg "Checking for mounted folder writability"
120 |
121 | if [ -d /data ]; then
122 | check_mounted_folder_writable_with_chown "/data"
123 | if [ -d /data/databases ]; then
124 | check_mounted_folder_writable_with_chown "/data/databases"
125 | fi
126 | if [ -d /data/dbms ]; then
127 | check_mounted_folder_writable_with_chown "/data/dbms"
128 | fi
129 | if [ -d /data/transactions ]; then
130 | check_mounted_folder_writable_with_chown "/data/transactions"
131 | fi
132 | fi
133 | if [ -d /backups ]; then
134 | check_mounted_folder_writable_with_chown "/backups"
135 | fi
136 |
137 | # ==== START NEO4J-ADMIN COMMAND ====
138 | if debugging_enabled; then
139 | echo ${exec_cmd} "${@}" --verbose
140 | ${exec_cmd} "${@}" --verbose
141 | else
142 | ${exec_cmd} "${@}"
143 | fi
144 |
```
--------------------------------------------------------------------------------
/crawled_pages.sql:
--------------------------------------------------------------------------------
```sql
1 | -- Enable the pgvector extension
2 | create extension if not exists vector;
3 |
4 | -- Drop tables if they exist (to allow rerunning the script)
5 | drop table if exists crawled_pages;
6 | drop table if exists code_examples;
7 | drop table if exists sources;
8 |
9 | -- Create the sources table
10 | create table sources (
11 | source_id text primary key,
12 | summary text,
13 | total_word_count integer default 0,
14 | created_at timestamp with time zone default timezone('utc'::text, now()) not null,
15 | updated_at timestamp with time zone default timezone('utc'::text, now()) not null
16 | );
17 |
18 | -- Create the documentation chunks table
19 | create table crawled_pages (
20 | id bigserial primary key,
21 | url varchar not null,
22 | chunk_number integer not null,
23 | content text not null,
24 | metadata jsonb not null default '{}'::jsonb,
25 | source_id text not null,
26 | embedding vector(1536), -- OpenAI embeddings are 1536 dimensions
27 | created_at timestamp with time zone default timezone('utc'::text, now()) not null,
28 |
29 | -- Add a unique constraint to prevent duplicate chunks for the same URL
30 | unique(url, chunk_number),
31 |
32 | -- Add foreign key constraint to sources table
33 | foreign key (source_id) references sources(source_id)
34 | );
35 |
36 | -- Create an index for better vector similarity search performance
37 | create index on crawled_pages using ivfflat (embedding vector_cosine_ops);
38 |
39 | -- Create an index on metadata for faster filtering
40 | create index idx_crawled_pages_metadata on crawled_pages using gin (metadata);
41 |
42 | -- Create an index on source_id for faster filtering
43 | CREATE INDEX idx_crawled_pages_source_id ON crawled_pages (source_id);
44 |
45 | -- Create a function to search for documentation chunks
46 | create or replace function match_crawled_pages (
47 | query_embedding vector(1536),
48 | match_count int default 10,
49 | filter jsonb DEFAULT '{}'::jsonb,
50 | source_filter text DEFAULT NULL
51 | ) returns table (
52 | id bigint,
53 | url varchar,
54 | chunk_number integer,
55 | content text,
56 | metadata jsonb,
57 | source_id text,
58 | similarity float
59 | )
60 | language plpgsql
61 | as $$
62 | #variable_conflict use_column
63 | begin
64 | return query
65 | select
66 | id,
67 | url,
68 | chunk_number,
69 | content,
70 | metadata,
71 | source_id,
72 | 1 - (crawled_pages.embedding <=> query_embedding) as similarity
73 | from crawled_pages
74 | where metadata @> filter
75 | AND (source_filter IS NULL OR source_id = source_filter)
76 | order by crawled_pages.embedding <=> query_embedding
77 | limit match_count;
78 | end;
79 | $$;
80 |
81 | -- Enable RLS on the crawled_pages table
82 | alter table crawled_pages enable row level security;
83 |
84 | -- Create a policy that allows anyone to read crawled_pages
85 | create policy "Allow public read access to crawled_pages"
86 | on crawled_pages
87 | for select
88 | to public
89 | using (true);
90 |
91 | -- Enable RLS on the sources table
92 | alter table sources enable row level security;
93 |
94 | -- Create a policy that allows anyone to read sources
95 | create policy "Allow public read access to sources"
96 | on sources
97 | for select
98 | to public
99 | using (true);
100 |
101 | -- Create the code_examples table
102 | create table code_examples (
103 | id bigserial primary key,
104 | url varchar not null,
105 | chunk_number integer not null,
106 | content text not null, -- The code example content
107 | summary text not null, -- Summary of the code example
108 | metadata jsonb not null default '{}'::jsonb,
109 | source_id text not null,
110 | embedding vector(1536), -- OpenAI embeddings are 1536 dimensions
111 | created_at timestamp with time zone default timezone('utc'::text, now()) not null,
112 |
113 | -- Add a unique constraint to prevent duplicate chunks for the same URL
114 | unique(url, chunk_number),
115 |
116 | -- Add foreign key constraint to sources table
117 | foreign key (source_id) references sources(source_id)
118 | );
119 |
120 | -- Create an index for better vector similarity search performance
121 | create index on code_examples using ivfflat (embedding vector_cosine_ops);
122 |
123 | -- Create an index on metadata for faster filtering
124 | create index idx_code_examples_metadata on code_examples using gin (metadata);
125 |
126 | -- Create an index on source_id for faster filtering
127 | CREATE INDEX idx_code_examples_source_id ON code_examples (source_id);
128 |
129 | -- Create a function to search for code examples
130 | create or replace function match_code_examples (
131 | query_embedding vector(1536),
132 | match_count int default 10,
133 | filter jsonb DEFAULT '{}'::jsonb,
134 | source_filter text DEFAULT NULL
135 | ) returns table (
136 | id bigint,
137 | url varchar,
138 | chunk_number integer,
139 | content text,
140 | summary text,
141 | metadata jsonb,
142 | source_id text,
143 | similarity float
144 | )
145 | language plpgsql
146 | as $$
147 | #variable_conflict use_column
148 | begin
149 | return query
150 | select
151 | id,
152 | url,
153 | chunk_number,
154 | content,
155 | summary,
156 | metadata,
157 | source_id,
158 | 1 - (code_examples.embedding <=> query_embedding) as similarity
159 | from code_examples
160 | where metadata @> filter
161 | AND (source_filter IS NULL OR source_id = source_filter)
162 | order by code_examples.embedding <=> query_embedding
163 | limit match_count;
164 | end;
165 | $$;
166 |
167 | -- Enable RLS on the code_examples table
168 | alter table code_examples enable row level security;
169 |
170 | -- Create a policy that allows anyone to read code_examples
171 | create policy "Allow public read access to code_examples"
172 | on code_examples
173 | for select
174 | to public
175 | using (true);
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/src/test/java/com/neo4j/docker/coredb/configurations/Configuration.java:
--------------------------------------------------------------------------------
```java
1 | package com.neo4j.docker.coredb.configurations;
2 |
3 | import com.neo4j.docker.utils.Neo4jVersion;
4 | import com.neo4j.docker.utils.TestSettings;
5 |
6 | import java.nio.file.Path;
7 | import java.nio.file.Paths;
8 | import java.util.EnumMap;
9 | import java.util.Map;
10 |
11 | public class Configuration
12 | {
13 | private static Map<Setting,Configuration> CONFIGURATIONS_5X = new EnumMap<Setting,Configuration>( Setting.class ) {{
14 | put( Setting.APOC_EXPORT_FILE_ENABLED, new Configuration( "apoc.export.file.enabled"));
15 | put( Setting.BACKUP_ENABLED, new Configuration("server.backup.enabled"));
16 | put( Setting.BACKUP_LISTEN_ADDRESS, new Configuration("server.backup.listen_address"));
17 | put( Setting.BROWSER_ALLOW_OUTGOING_CONNECTIONS, new Configuration("browser.allow_outgoing_connections"));
18 | put( Setting.CLUSTER_RAFT_ADDRESS, new Configuration("server.cluster.raft.advertised_address"));
19 | put( Setting.CLUSTER_ROUTING_ADDRESS, new Configuration("server.routing.advertised_address"));
20 | put( Setting.CLUSTER_TRANSACTION_ADDRESS, new Configuration("server.cluster.advertised_address"));
21 | put( Setting.DEFAULT_LISTEN_ADDRESS, new Configuration("server.default_listen_address"));
22 | put( Setting.DIRECTORIES_DATA, new Configuration("server.directories.data"));
23 | put( Setting.DIRECTORIES_LOGS, new Configuration("server.directories.logs"));
24 | put( Setting.DIRECTORIES_METRICS, new Configuration("server.directories.metrics"));
25 | put( Setting.JVM_ADDITIONAL, new Configuration("server.jvm.additional"));
26 | put( Setting.LOGS_GC_ROTATION_KEEPNUMBER, new Configuration( "server.logs.gc.rotation.keep_number"));
27 | put( Setting.MEMORY_HEAP_INITIALSIZE, new Configuration("server.memory.heap.initial_size"));
28 | put( Setting.MEMORY_HEAP_MAXSIZE, new Configuration( "server.memory.heap.max_size"));
29 | put( Setting.MEMORY_PAGECACHE_SIZE, new Configuration("server.memory.pagecache.size"));
30 | put( Setting.MINIMUM_PASSWORD_LENGTH, new Configuration("dbms.security.auth_minimum_password_length"));
31 | put( Setting.SECURITY_PROCEDURES_UNRESTRICTED, new Configuration("dbms.security.procedures.unrestricted"));
32 | put( Setting.TXLOG_RETENTION_POLICY, new Configuration("db.tx_log.rotation.retention_policy"));
33 | }};
34 |
35 | private static Map<Setting,Configuration> CONFIGURATIONS_4X = new EnumMap<Setting,Configuration>( Setting.class ) {{
36 | put( Setting.APOC_EXPORT_FILE_ENABLED, new Configuration( "apoc.export.file.enabled"));
37 | put( Setting.BACKUP_ENABLED, new Configuration("dbms.backup.enabled"));
38 | put( Setting.BACKUP_LISTEN_ADDRESS, new Configuration("dbms.backup.listen_address"));
39 | put( Setting.BROWSER_ALLOW_OUTGOING_CONNECTIONS, new Configuration("browser.allow_outgoing_connections"));
40 | put( Setting.CLUSTER_DISCOVERY_ADDRESS, new Configuration("causal_clustering.discovery_advertised_address"));
41 | put( Setting.CLUSTER_RAFT_ADDRESS, new Configuration("causal_clustering.raft_advertised_address"));
42 | put( Setting.CLUSTER_ROUTING_ADDRESS, new Configuration("dbms.routing.advertised_address"));
43 | put( Setting.CLUSTER_TRANSACTION_ADDRESS, new Configuration("causal_clustering.transaction_advertised_address"));
44 | put( Setting.DEFAULT_LISTEN_ADDRESS, new Configuration("dbms.default_listen_address"));
45 | put( Setting.DIRECTORIES_DATA, new Configuration("dbms.directories.data"));
46 | put( Setting.DIRECTORIES_LOGS, new Configuration("dbms.directories.logs"));
47 | put( Setting.DIRECTORIES_METRICS, new Configuration("dbms.directories.metrics"));
48 | put( Setting.JVM_ADDITIONAL, new Configuration("dbms.jvm.additional"));
49 | put( Setting.LOGS_GC_ROTATION_KEEPNUMBER, new Configuration( "dbms.logs.gc.rotation.keep_number"));
50 | put( Setting.MEMORY_HEAP_INITIALSIZE, new Configuration("dbms.memory.heap.initial_size"));
51 | put( Setting.MEMORY_HEAP_MAXSIZE, new Configuration("dbms.memory.heap.max_size"));
52 | put( Setting.MEMORY_PAGECACHE_SIZE, new Configuration("dbms.memory.pagecache.size"));
53 | put( Setting.SECURITY_PROCEDURES_UNRESTRICTED, new Configuration("dbms.security.procedures.unrestricted"));
54 | put( Setting.TXLOG_RETENTION_POLICY, new Configuration("dbms.tx_log.rotation.retention_policy"));
55 | }};
56 | public static Map<Setting,Configuration> getConfigurationNameMap()
57 | {
58 | return getConfigurationNameMap( TestSettings.NEO4J_VERSION );
59 | }
60 |
61 | public static Map<Setting,Configuration> getConfigurationNameMap( Neo4jVersion version )
62 | {
63 | switch ( version.major )
64 | {
65 | case 3:
66 | EnumMap<Setting,Configuration> out = new EnumMap<Setting,Configuration>( CONFIGURATIONS_4X );
67 | out.put( Setting.DEFAULT_LISTEN_ADDRESS, new Configuration( "dbms.connectors.default_listen_address" ) );
68 | return out;
69 | case 4:
70 | return CONFIGURATIONS_4X;
71 | default:
72 | return CONFIGURATIONS_5X;
73 | }
74 | }
75 | public static Path getConfigurationResourcesFolder()
76 | {
77 | return getConfigurationResourcesFolder(TestSettings.NEO4J_VERSION);
78 | }
79 |
80 | public static Path getConfigurationResourcesFolder( Neo4jVersion version )
81 | {
82 | if(version.isAtLeastVersion( Neo4jVersion.NEO4J_VERSION_500 ))
83 | {
84 | return Paths.get("src", "test", "resources", "confs");
85 | }
86 | else return Paths.get( "src", "test", "resources", "confs", "before50");
87 | }
88 |
89 | public String name;
90 | public String envName;
91 |
92 | private Configuration( String name )
93 | {
94 | this.name = name;
95 | this.envName = "NEO4J_" + name.replace( '_', '-' )
96 | .replace( '.', '_')
97 | .replace( "-", "__" );
98 | }
99 | }
100 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/docker-image-src/3.0/docker-entrypoint.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash -eu
2 |
3 | setting() {
4 | setting="${1}"
5 | value="${2}"
6 | file="${3:-neo4j.conf}"
7 |
8 | if [ ! -f "conf/${file}" ]; then
9 | if [ -f "conf/neo4j.conf" ]; then
10 | file="neo4j.conf"
11 | fi
12 | fi
13 |
14 | # Don't allow settings with no value or settings that start with a number (neo4j converts settings to env variables and you cannot have an env variable that starts with a number)
15 | if [[ -n ${value} ]]; then
16 | if [[ ! "${setting}" =~ ^[0-9]+.*$ ]]; then
17 | if grep --quiet --fixed-strings "${setting}=" conf/"${file}"; then
18 | sed --in-place "s|.*${setting}=.*|${setting}=${value}|" conf/"${file}"
19 | else
20 | echo "${setting}=${value}" >>conf/"${file}"
21 | fi
22 | else
23 | echo >&2 "WARNING: ${setting} not written to conf file because settings that start with a number are not permitted"
24 | fi
25 | fi
26 | }
27 |
28 | cmd="$1"
29 |
30 | if [ "${cmd}" == "dump-config" ]; then
31 | if [ -d /conf ]; then
32 | cp --recursive conf/* /conf
33 | exit 0
34 | else
35 | echo >&2 "You must provide a /conf volume"
36 | exit 1
37 | fi
38 | fi
39 |
40 | # Env variable naming convention:
41 | # - prefix NEO4J_
42 | # - double underscore char '__' instead of single underscore '_' char in the setting name
43 | # - underscore char '_' instead of dot '.' char in the setting name
44 | # Example:
45 | # NEO4J_dbms_tx__log_rotation_retention__policy env variable to set
46 | # dbms.tx_log.rotation.retention_policy setting
47 |
48 | # Backward compatibility - map old hardcoded env variables into new naming convention (if they aren't set already)
49 | # Set some to default values if unset
50 | : ${NEO4J_dbms_tx__log_rotation_retention__policy:=${NEO4J_dbms_txLog_rotation_retentionPolicy:-"100M size"}}
51 | : ${NEO4J_wrapper_java_additional:=${NEO4J_UDC_SOURCE:-"-Dneo4j.ext.udc.source=docker"}}
52 | : ${NEO4J_dbms_memory_heap_initial__size:=${NEO4J_dbms_memory_heap_maxSize:-"512"}}
53 | : ${NEO4J_dbms_memory_heap_max__size:=${NEO4J_dbms_memory_heap_maxSize:-"512"}}
54 | : ${NEO4J_dbms_unmanaged__extension__classes:=${NEO4J_dbms_unmanagedExtensionClasses:-}}
55 | : ${NEO4J_dbms_allow__format__migration:=${NEO4J_dbms_allowFormatMigration:-}}
56 | : ${NEO4J_ha_server__id:=${NEO4J_ha_serverId:-}}
57 | : ${NEO4J_ha_initial__hosts:=${NEO4J_ha_initialHosts:-}}
58 |
59 | : ${NEO4J_dbms_connector_http_address:="0.0.0.0:7474"}
60 | : ${NEO4J_dbms_connector_https_address:="0.0.0.0:7473"}
61 | : ${NEO4J_dbms_connector_bolt_address:="0.0.0.0:7687"}
62 | : ${NEO4J_ha_host_coordination:="$(hostname):5001"}
63 | : ${NEO4J_ha_host_data:="$(hostname):6001"}
64 |
65 | # unset old hardcoded unsupported env variables
66 | unset NEO4J_dbms_txLog_rotation_retentionPolicy NEO4J_UDC_SOURCE \
67 | NEO4J_dbms_memory_heap_maxSize NEO4J_dbms_memory_heap_maxSize \
68 | NEO4J_dbms_unmanagedExtensionClasses NEO4J_dbms_allowFormatMigration \
69 | NEO4J_ha_initialHosts
70 |
71 | if [ -d /conf ]; then
72 | find /conf -type f -exec cp {} conf \;
73 | fi
74 |
75 | if [ -d /ssl ]; then
76 | NEO4J_dbms_directories_certificates="/ssl"
77 | fi
78 |
79 | if [ -d /plugins ]; then
80 | NEO4J_dbms_directories_plugins="/plugins"
81 | fi
82 |
83 | if [ -d /logs ]; then
84 | NEO4J_dbms_directories_logs="/logs"
85 | fi
86 |
87 | if [ -d /import ]; then
88 | NEO4J_dbms_directories_import="/import"
89 | fi
90 |
91 | if [ -d /metrics ]; then
92 | NEO4J_dbms_directories_metrics="/metrics"
93 | fi
94 |
95 | if [ "${cmd}" == "neo4j" ] ; then
96 | if [ "${NEO4J_AUTH:-}" == "none" ]; then
97 | NEO4J_dbms_security_auth__enabled=false
98 | elif [[ "${NEO4J_AUTH:-}" == neo4j/* ]]; then
99 | password="${NEO4J_AUTH#neo4j/}"
100 | if [ "${password}" == "neo4j" ]; then
101 | echo "Invalid value for password. It cannot be 'neo4j', which is the default."
102 | exit 1
103 | fi
104 |
105 | setting "dbms.connector.http.address" "127.0.0.1:7474"
106 | setting "dbms.connector.https.address" "127.0.0.1:7473"
107 | setting "dbms.connector.bolt.address" "127.0.0.1:7687"
108 | bin/neo4j start || \
109 | (cat logs/neo4j.log && echo "Neo4j failed to start for password change" && exit 1)
110 |
111 | end="$((SECONDS+100))"
112 | while true; do
113 | http_code="$(curl --silent --write-out %{http_code} --user "neo4j:${password}" --output /dev/null http://localhost:7474/db/data/ || true)"
114 |
115 | if [[ "${http_code}" = "200" ]]; then
116 | break;
117 | fi
118 |
119 | if [[ "${http_code}" = "401" ]]; then
120 | curl --fail --silent --show-error --user neo4j:neo4j \
121 | --data '{"password": "'"${password}"'"}' \
122 | --header 'Content-Type: application/json' \
123 | http://localhost:7474/user/neo4j/password
124 | break;
125 | fi
126 |
127 | if [[ "${SECONDS}" -ge "${end}" ]]; then
128 | (cat logs/neo4j.log && echo "Neo4j failed to start" && exit 1)
129 | fi
130 |
131 | sleep 1
132 | done
133 |
134 | bin/neo4j stop
135 | elif [ -n "${NEO4J_AUTH:-}" ]; then
136 | echo "Invalid value for NEO4J_AUTH: '${NEO4J_AUTH}'"
137 | exit 1
138 | fi
139 | fi
140 |
141 | # list env variables with prefix NEO4J_ and create settings from them
142 | unset NEO4J_AUTH NEO4J_SHA256 NEO4J_TARBALL
143 | for i in $( set | grep ^NEO4J_ | awk -F'=' '{print $1}' | sort -rn ); do
144 | setting=$(echo ${i} | sed 's|^NEO4J_||' | sed 's|_|.|g' | sed 's|\.\.|_|g')
145 | value=$(echo ${!i})
146 | if [[ -n ${value} ]]; then
147 | if [[ ! "${setting}" =~ ^[0-9]+.*$ ]]; then
148 | if grep -q -F "${setting}=" conf/neo4j.conf; then
149 | # Remove any lines containing the setting already
150 | sed --in-place "/^${setting}=.*/d" conf/neo4j.conf
151 | fi
152 | # Then always append setting to file
153 | echo "${setting}=${value}" >> conf/neo4j.conf
154 | else
155 | echo >&2 "WARNING: ${setting} not written to conf file because settings that start with a number are not permitted"
156 | fi
157 | fi
158 | done
159 |
160 | [ -f "${EXTENSION_SCRIPT:-}" ] && . ${EXTENSION_SCRIPT}
161 |
162 | if [ "${cmd}" == "neo4j" ] ; then
163 | exec bin/neo4j console
164 | else
165 | exec "$@"
166 | fi
167 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/src/test/java/com/neo4j/docker/neo4jadmin/TestDumpLoad.java:
--------------------------------------------------------------------------------
```java
1 | package com.neo4j.docker.neo4jadmin;
2 |
3 | import com.neo4j.docker.utils.DatabaseIO;
4 | import com.neo4j.docker.utils.Neo4jVersion;
5 | import com.neo4j.docker.utils.SetContainerUser;
6 | import com.neo4j.docker.utils.WaitStrategies;
7 | import com.neo4j.docker.utils.TemporaryFolderManager;
8 | import com.neo4j.docker.utils.TestSettings;
9 | import org.junit.jupiter.api.Assertions;
10 | import org.junit.jupiter.api.Assumptions;
11 | import org.junit.jupiter.api.BeforeAll;
12 | import org.junit.jupiter.api.Test;
13 | import org.junit.jupiter.api.extension.RegisterExtension;
14 | import org.slf4j.Logger;
15 | import org.slf4j.LoggerFactory;
16 | import org.testcontainers.containers.GenericContainer;
17 | import org.testcontainers.containers.output.Slf4jLogConsumer;
18 | import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy;
19 | import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
20 |
21 | import java.nio.file.Path;
22 | import java.time.Duration;
23 |
24 | public class TestDumpLoad
25 | {
26 | private static Logger log = LoggerFactory.getLogger( TestDumpLoad.class );
27 | @RegisterExtension
28 | public static TemporaryFolderManager temporaryFolderManager = new TemporaryFolderManager();
29 |
30 | @BeforeAll
31 | static void beforeAll()
32 | {
33 | Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( Neo4jVersion.NEO4J_VERSION_500 ),
34 | "These tests only apply to neo4j-admin images of 5.0 and greater");
35 | }
36 |
37 | private GenericContainer createDBContainer( boolean asDefaultUser, String password )
38 | {
39 | String auth = "none";
40 | if(!password.equalsIgnoreCase("none"))
41 | {
42 | auth = "neo4j/"+password;
43 | }
44 |
45 | GenericContainer container = new GenericContainer( TestSettings.IMAGE_ID );
46 | container.withEnv( "NEO4J_AUTH", auth )
47 | .withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
48 | .withExposedPorts( 7474, 7687 )
49 | .withLogConsumer( new Slf4jLogConsumer( log ) )
50 | .waitingFor( WaitStrategies.waitForNeo4jReady( password) );
51 | if(!asDefaultUser)
52 | {
53 | SetContainerUser.nonRootUser( container );
54 | }
55 | return container;
56 | }
57 |
58 | private GenericContainer createAdminContainer( boolean asDefaultUser )
59 | {
60 | GenericContainer container = new GenericContainer( TestSettings.ADMIN_IMAGE_ID );
61 | container.withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
62 | .withExposedPorts( 7474, 7687 )
63 | .withLogConsumer( new Slf4jLogConsumer( log ) )
64 | .waitingFor( new LogMessageWaitStrategy().withRegEx( "^Done: \\d+ files, [\\d\\.,]+[KMGi]*B processed.*" ) )
65 | // .waitingFor( new LogMessageWaitStrategy().withRegEx( "^Done: .*" ) )
66 | .withStartupCheckStrategy( new OneShotStartupCheckStrategy().withTimeout( Duration.ofSeconds( 90 ) ) );
67 | if(!asDefaultUser)
68 | {
69 | SetContainerUser.nonRootUser( container );
70 | }
71 | return container;
72 | }
73 |
74 | @Test
75 | void shouldDumpAndLoad_defaultUser_noAuth() throws Exception
76 | {
77 | shouldCreateDumpAndLoadDump( true, "none" );
78 | }
79 |
80 | @Test
81 | void shouldDumpAndLoad_nonDefaultUser_noAuth() throws Exception
82 | {
83 | shouldCreateDumpAndLoadDump( false, "none" );
84 | }
85 |
86 | @Test
87 | void shouldDumpAndLoad_defaultUser_withAuth() throws Exception
88 | {
89 | shouldCreateDumpAndLoadDump( true, "verysecretpassword" );
90 | }
91 |
92 | @Test
93 | void shouldDumpAndLoad_nonDefaultUser_withAuth() throws Exception
94 | {
95 | shouldCreateDumpAndLoadDump( false, "verysecretpassword" );
96 | }
97 |
98 | private void shouldCreateDumpAndLoadDump( boolean asDefaultUser, String password ) throws Exception
99 | {
100 | Path firstDataDir;
101 | Path secondDataDir;
102 | Path backupDir;
103 |
104 | // start a database and populate it
105 | try(GenericContainer container = createDBContainer( asDefaultUser, password ))
106 | {
107 | firstDataDir = temporaryFolderManager.createNamedFolderAndMountAsVolume(container,"data1", "/data");
108 | container.start();
109 | DatabaseIO dbio = new DatabaseIO( container );
110 | dbio.putInitialDataIntoContainer( "neo4j", password );
111 | container.getDockerClient().stopContainerCmd( container.getContainerId() ).withTimeout(30).exec();
112 | }
113 |
114 | // use admin container to create dump
115 | try(GenericContainer admin = createAdminContainer( asDefaultUser ))
116 | {
117 | temporaryFolderManager.mountHostFolderAsVolume( admin, firstDataDir, "/data" );
118 | backupDir = temporaryFolderManager.createFolderAndMountAsVolume(admin, "/backups");
119 | admin.withCommand( "neo4j-admin", "database", "dump", "neo4j", "--to-path=/backups" );
120 | admin.start();
121 | }
122 | Assertions.assertTrue( backupDir.resolve( "neo4j.dump" ).toFile().exists(), "dump file not created");
123 |
124 | // dump file exists. Now try to load it into a new database.
125 | // use admin container to create dump
126 | try(GenericContainer admin = createAdminContainer( asDefaultUser ))
127 | {
128 | secondDataDir = temporaryFolderManager.createNamedFolderAndMountAsVolume(admin, "data2", "/data");
129 | temporaryFolderManager.mountHostFolderAsVolume( admin, backupDir, "/backups" );
130 | admin.withCommand( "neo4j-admin", "database", "load", "neo4j", "--from-path=/backups" );
131 | admin.start();
132 | }
133 |
134 | // verify data in 2nd data directory by starting a database and verifying data we populated earlier
135 | try(GenericContainer container = createDBContainer( asDefaultUser, password ))
136 | {
137 | temporaryFolderManager.mountHostFolderAsVolume( container, secondDataDir, "/data" );
138 | container.start();
139 | DatabaseIO dbio = new DatabaseIO( container );
140 | dbio.verifyInitialDataInContainer( "neo4j", password );
141 | }
142 | }
143 | }
144 |
```
--------------------------------------------------------------------------------
/knowledge_graphs/test_script.py:
--------------------------------------------------------------------------------
```python
1 | from __future__ import annotations
2 | from typing import Dict, List, Optional
3 | from dataclasses import dataclass
4 | from pydantic import BaseModel, Field
5 | from dotenv import load_dotenv
6 | from rich.markdown import Markdown
7 | from rich.console import Console
8 | from rich.live import Live
9 | import asyncio
10 | import os
11 |
12 | from pydantic_ai.providers.openai import OpenAIProvider
13 | from pydantic_ai.models.openai import OpenAIModel
14 | from pydantic_ai import Agent, RunContext
15 | from graphiti_core import Graphiti
16 |
17 | load_dotenv()
18 |
19 | # ========== Define dependencies ==========
20 | @dataclass
21 | class GraphitiDependencies:
22 | """Dependencies for the Graphiti agent."""
23 | graphiti_client: Graphiti
24 |
25 | # ========== Helper function to get model configuration ==========
26 | def get_model():
27 | """Configure and return the LLM model to use."""
28 | model_choice = os.getenv('MODEL_CHOICE', 'gpt-4.1-mini')
29 | api_key = os.getenv('OPENAI_API_KEY', 'no-api-key-provided')
30 |
31 | return OpenAIModel(model_choice, provider=OpenAIProvider(api_key=api_key))
32 |
33 | # ========== Create the Graphiti agent ==========
34 | graphiti_agent = Agent(
35 | get_model(),
36 | system_prompt="""You are a helpful assistant with access to a knowledge graph filled with temporal data about LLMs.
37 | When the user asks you a question, use your search tool to query the knowledge graph and then answer honestly.
38 | Be willing to admit when you didn't find the information necessary to answer the question.""",
39 | deps_type=GraphitiDependencies
40 | )
41 |
42 | # ========== Define a result model for Graphiti search ==========
43 | class GraphitiSearchResult(BaseModel):
44 | """Model representing a search result from Graphiti."""
45 | uuid: str = Field(description="The unique identifier for this fact")
46 | fact: str = Field(description="The factual statement retrieved from the knowledge graph")
47 | valid_at: Optional[str] = Field(None, description="When this fact became valid (if known)")
48 | invalid_at: Optional[str] = Field(None, description="When this fact became invalid (if known)")
49 | source_node_uuid: Optional[str] = Field(None, description="UUID of the source node")
50 |
51 | # ========== Graphiti search tool ==========
52 | @graphiti_agent.tool
53 | async def search_graphiti(ctx: RunContext[GraphitiDependencies], query: str) -> List[GraphitiSearchResult]:
54 | """Search the Graphiti knowledge graph with the given query.
55 |
56 | Args:
57 | ctx: The run context containing dependencies
58 | query: The search query to find information in the knowledge graph
59 |
60 | Returns:
61 | A list of search results containing facts that match the query
62 | """
63 | # Access the Graphiti client from dependencies
64 | graphiti = ctx.deps.graphiti_client
65 |
66 | try:
67 | # Perform the search
68 | results = await graphiti.search(query)
69 |
70 | # Format the results
71 | formatted_results = []
72 | for result in results:
73 | formatted_result = GraphitiSearchResult(
74 | uuid=result.uuid,
75 | fact=result.fact,
76 | source_node_uuid=result.source_node_uuid if hasattr(result, 'source_node_uuid') else None
77 | )
78 |
79 | # Add temporal information if available
80 | if hasattr(result, 'valid_at') and result.valid_at:
81 | formatted_result.valid_at = str(result.valid_at)
82 | if hasattr(result, 'invalid_at') and result.invalid_at:
83 | formatted_result.invalid_at = str(result.invalid_at)
84 |
85 | formatted_results.append(formatted_result)
86 |
87 | return formatted_results
88 | except Exception as e:
89 | # Log the error but don't close the connection since it's managed by the dependency
90 | print(f"Error searching Graphiti: {str(e)}")
91 | raise
92 |
93 | # ========== Main execution function ==========
94 | async def main():
95 | """Run the Graphiti agent with user queries."""
96 | print("Graphiti Agent - Powered by Pydantic AI, Graphiti, and Neo4j")
97 | print("Enter 'exit' to quit the program.")
98 |
99 | # Neo4j connection parameters
100 | neo4j_uri = os.environ.get('NEO4J_URI', 'bolt://localhost:7687')
101 | neo4j_user = os.environ.get('NEO4J_USER', 'neo4j')
102 | neo4j_password = os.environ.get('NEO4J_PASSWORD', 'password')
103 |
104 | # Initialize Graphiti with Neo4j connection
105 | graphiti_client = Graphiti(neo4j_uri, neo4j_user, neo4j_password)
106 |
107 | # Initialize the graph database with graphiti's indices if needed
108 | try:
109 | await graphiti_client.build_indices_and_constraints()
110 | print("Graphiti indices built successfully.")
111 | except Exception as e:
112 | print(f"Note: {str(e)}")
113 | print("Continuing with existing indices...")
114 |
115 | console = Console()
116 | messages = []
117 |
118 | try:
119 | while True:
120 | # Get user input
121 | user_input = input("\n[You] ")
122 |
123 | # Check if user wants to exit
124 | if user_input.lower() in ['exit', 'quit', 'bye', 'goodbye']:
125 | print("Goodbye!")
126 | break
127 |
128 | try:
129 | # Process the user input and output the response
130 | print("\n[Assistant]")
131 | with Live('', console=console, vertical_overflow='visible') as live:
132 | # Pass the Graphiti client as a dependency
133 | deps = GraphitiDependencies(graphiti_client=graphiti_client)
134 |
135 | async with graphiti_agent.run_a_stream(
136 | user_input, message_history=messages, deps=deps
137 | ) as result:
138 | curr_message = ""
139 | async for message in result.stream_text(delta=True):
140 | curr_message += message
141 | live.update(Markdown(curr_message))
142 |
143 | # Add the new messages to the chat history
144 | messages.extend(result.all_messages())
145 |
146 | except Exception as e:
147 | print(f"\n[Error] An error occurred: {str(e)}")
148 | finally:
149 | # Close the Graphiti connection when done
150 | await graphiti_client.close()
151 | print("\nGraphiti connection closed.")
152 |
153 | if __name__ == "__main__":
154 | try:
155 | asyncio.run(main())
156 | except KeyboardInterrupt:
157 | print("\nProgram terminated by user.")
158 | except Exception as e:
159 | print(f"\nUnexpected error: {str(e)}")
160 | raise
161 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/src/test/java/com/neo4j/docker/TestDeprecationWarning.java:
--------------------------------------------------------------------------------
```java
1 | package com.neo4j.docker;
2 |
3 | import com.neo4j.docker.utils.TestSettings;
4 | import com.neo4j.docker.utils.WaitStrategies;
5 | import org.junit.jupiter.api.Assertions;
6 | import org.junit.jupiter.api.Assumptions;
7 | import org.junit.jupiter.api.Test;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 | import org.testcontainers.containers.GenericContainer;
11 | import org.testcontainers.containers.output.OutputFrame;
12 | import org.testcontainers.containers.output.Slf4jLogConsumer;
13 |
14 | import java.time.Duration;
15 |
16 | public class TestDeprecationWarning
17 | {
18 | private final Logger log = LoggerFactory.getLogger( TestDeprecationWarning.class );
19 | private static final String DEPRECATION_WARN_STRING = "Neo4j Red Hat UBI8 images are deprecated in favour of Red Hat UBI9";
20 | private static final String DEPRECATION_WARN_SUPPRESS_FLAG = "NEO4J_DEPRECATION_WARNING";
21 |
22 | // The opposite test to make sure there are no deprecation warnings in non-ubi8 images already exists at
23 | // com.neo4j.docker.coredb.TestBasic.testNoUnexpectedErrors
24 | @Test
25 | void shouldWarnIfUsingDeprecatedBaseOS_coreDB() throws Exception
26 | {
27 | Assumptions.assumeTrue( TestSettings.BASE_OS == TestSettings.BaseOS.UBI8,
28 | "Deprecation warning should only exist in UBI8 images");
29 | try(GenericContainer container = new GenericContainer(TestSettings.IMAGE_ID))
30 | {
31 | container.withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
32 | .withExposedPorts( 7474, 7687 )
33 | .withLogConsumer( new Slf4jLogConsumer( log ))
34 | .waitingFor( WaitStrategies.waitForBoltReady() );
35 | container.start();
36 | // container should successfully start
37 | String logs = container.getLogs( OutputFrame.OutputType.STDERR );
38 | Assertions.assertTrue( logs.contains( DEPRECATION_WARN_STRING ),
39 | "Container did not warn about ubi8 deprecation. Actual error logs:\n"+logs);
40 | }
41 | }
42 |
43 | @Test
44 | void shouldWarnIfUsingDeprecatedBaseOS_admin()
45 | {
46 | Assumptions.assumeTrue( TestSettings.BASE_OS == TestSettings.BaseOS.UBI8,
47 | "Deprecation warning should only exist in UBI8 images");
48 | try(GenericContainer container = new GenericContainer(TestSettings.ADMIN_IMAGE_ID))
49 | {
50 | container.withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
51 | .withExposedPorts( 7474, 7687 )
52 | .withLogConsumer( new Slf4jLogConsumer( log ))
53 | .withCommand( "neo4j-admin", "--help" );
54 | WaitStrategies.waitUntilContainerFinished( container, Duration.ofSeconds( 30 ));
55 | container.start();
56 | // container should successfully start
57 | String logs = container.getLogs( OutputFrame.OutputType.STDERR );
58 | Assertions.assertTrue( logs.contains( DEPRECATION_WARN_STRING ),
59 | "Container did not warn about ubi8 deprecation. Actual error logs:\n"+logs);
60 | }
61 | }
62 |
63 | // this is a requirement for docker official images, so doesn't need testing for neo4j-admin
64 | @Test
65 | void shouldOnlyWarnWhenRunningNeo4jCommands() throws Exception
66 | {
67 | Assumptions.assumeTrue( TestSettings.BASE_OS == TestSettings.BaseOS.UBI8,
68 | "Deprecation warning should only exist in UBI8 images");
69 | try(GenericContainer container = new GenericContainer(TestSettings.IMAGE_ID))
70 | {
71 | container.withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
72 | .withExposedPorts( 7474, 7687 )
73 | .withLogConsumer( new Slf4jLogConsumer( log ))
74 | .withCommand( "cat", "/etc/os-release" );
75 | WaitStrategies.waitUntilContainerFinished( container, Duration.ofSeconds( 30 ) );
76 | container.start();
77 | // container should successfully start
78 | String logs = container.getLogs( OutputFrame.OutputType.STDERR );
79 | Assertions.assertFalse( logs.contains( DEPRECATION_WARN_STRING ),
80 | "Container should not have warned about ubi8 deprecation. Actual error logs:\n"+logs);
81 | }
82 | }
83 |
84 | @Test
85 | void shouldIgnoreDeprecationSuppression_coreDB() throws Exception
86 | {
87 | Assumptions.assumeTrue( TestSettings.BASE_OS == TestSettings.BaseOS.UBI8,
88 | "Deprecation warning should only exist in UBI8 images");
89 | try(GenericContainer container = new GenericContainer(TestSettings.IMAGE_ID))
90 | {
91 | container.withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
92 | .withEnv( DEPRECATION_WARN_SUPPRESS_FLAG, "suppress" )
93 | .withExposedPorts( 7474, 7687 )
94 | .withLogConsumer( new Slf4jLogConsumer( log ))
95 | .waitingFor( WaitStrategies.waitForBoltReady() );
96 | container.start();
97 | // container should successfully start
98 | String logs = container.getLogs( OutputFrame.OutputType.STDERR );
99 | Assertions.assertTrue( logs.contains( DEPRECATION_WARN_STRING ),
100 | "Container did not warn about ubi8 deprecation. Actual error logs:\n"+logs);
101 | }
102 | }
103 |
104 | @Test
105 | void shouldIgnoreDeprecationSuppressed_admin()
106 | {
107 | Assumptions.assumeTrue( TestSettings.BASE_OS == TestSettings.BaseOS.UBI8,
108 | "Deprecation warning should only exist in UBI8 images");
109 | try(GenericContainer container = new GenericContainer(TestSettings.ADMIN_IMAGE_ID))
110 | {
111 | container.withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
112 | .withEnv( DEPRECATION_WARN_SUPPRESS_FLAG, "suppress" )
113 | .withExposedPorts( 7474, 7687 )
114 | .withLogConsumer( new Slf4jLogConsumer( log ))
115 | .withCommand( "neo4j-admin", "--help" );
116 | WaitStrategies.waitUntilContainerFinished( container, Duration.ofSeconds( 30 ));
117 | container.start();
118 | // container should successfully start
119 | String logs = container.getLogs( OutputFrame.OutputType.STDERR );
120 | Assertions.assertTrue( logs.contains( DEPRECATION_WARN_STRING ),
121 | "Container did not warn about ubi8 deprecation. Actual error logs:\n"+logs);
122 | }
123 | }
124 | }
125 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/src/test/java/com/neo4j/docker/neo4jadmin/TestBackupRestore44.java:
--------------------------------------------------------------------------------
```java
1 | package com.neo4j.docker.neo4jadmin;
2 |
3 | import com.neo4j.docker.coredb.configurations.Configuration;
4 | import com.neo4j.docker.coredb.configurations.Setting;
5 | import com.neo4j.docker.utils.DatabaseIO;
6 | import com.neo4j.docker.utils.Neo4jVersion;
7 | import com.neo4j.docker.utils.SetContainerUser;
8 | import com.neo4j.docker.utils.WaitStrategies;
9 | import com.neo4j.docker.utils.TemporaryFolderManager;
10 | import com.neo4j.docker.utils.TestSettings;
11 | import org.junit.jupiter.api.Assertions;
12 | import org.junit.jupiter.api.Assumptions;
13 | import org.junit.jupiter.api.BeforeAll;
14 | import org.junit.jupiter.api.Test;
15 | import org.junit.jupiter.api.extension.RegisterExtension;
16 | import org.slf4j.Logger;
17 | import org.slf4j.LoggerFactory;
18 | import org.testcontainers.containers.GenericContainer;
19 | import org.testcontainers.containers.output.Slf4jLogConsumer;
20 | import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
21 |
22 | import java.nio.file.Path;
23 | import java.time.Duration;
24 | import java.util.Map;
25 |
26 | public class TestBackupRestore44
27 | {
28 | // with authentication
29 | // with non-default user
30 | private final Logger log = LoggerFactory.getLogger( TestBackupRestore44.class );
31 | @RegisterExtension
32 | public static TemporaryFolderManager temporaryFolderManager = new TemporaryFolderManager();
33 |
34 | @BeforeAll
35 | static void beforeAll()
36 | {
37 | Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( Neo4jVersion.NEO4J_VERSION_440),
38 | "Neo4j admin image not available before 4.4.0");
39 | Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isOlderThan( Neo4jVersion.NEO4J_VERSION_500 ),
40 | "These Neo4j admin tests are only for 4.4");
41 | Assumptions.assumeTrue( TestSettings.EDITION == TestSettings.Edition.ENTERPRISE,
42 | "backup and restore only available in Neo4j Enterprise" );
43 | }
44 |
45 | private GenericContainer createDBContainer( boolean asDefaultUser, String password )
46 | {
47 | String auth = "none";
48 | if(!password.equalsIgnoreCase("none"))
49 | {
50 | auth = "neo4j/"+password;
51 | }
52 | Map<Setting,Configuration> confNames = Configuration.getConfigurationNameMap();
53 | GenericContainer container = new GenericContainer( TestSettings.IMAGE_ID );
54 | container.withEnv( "NEO4J_AUTH", auth )
55 | .withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
56 | .withEnv( confNames.get( Setting.BACKUP_ENABLED ).envName, "true" )
57 | .withEnv( confNames.get( Setting.BACKUP_LISTEN_ADDRESS ).envName, "0.0.0.0:6362" )
58 | .withExposedPorts( 7474, 7687, 6362 )
59 | .withLogConsumer( new Slf4jLogConsumer( log ) )
60 | .waitingFor(WaitStrategies.waitForNeo4jReady( password ));
61 | if(!asDefaultUser)
62 | {
63 | SetContainerUser.nonRootUser( container );
64 | }
65 | return container;
66 | }
67 |
68 | private GenericContainer createAdminContainer( boolean asDefaultUser )
69 | {
70 | GenericContainer container = new GenericContainer( TestSettings.ADMIN_IMAGE_ID );
71 | container.withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
72 | .withLogConsumer( new Slf4jLogConsumer( log ) );
73 | WaitStrategies.waitUntilContainerFinished( container, Duration.ofSeconds( 180) );
74 | if(!asDefaultUser)
75 | {
76 | SetContainerUser.nonRootUser( container );
77 | }
78 | return container;
79 | }
80 |
81 | @Test
82 | void shouldBackupAndRestore_defaultUser_noAuth() throws Exception
83 | {
84 | testCanBackupAndRestore( true, "none" );
85 | }
86 | @Test
87 | void shouldBackupAndRestore_nonDefaultUser_noAuth() throws Exception
88 | {
89 | testCanBackupAndRestore( false, "none" );
90 | }
91 | @Test
92 | void shouldBackupAndRestore_defaultUser_withAuth() throws Exception
93 | {
94 | testCanBackupAndRestore( true, "secretpassword" );
95 | }
96 | @Test
97 | void shouldBackupAndRestore_nonDefaultUser_withAuth() throws Exception
98 | {
99 | testCanBackupAndRestore( false, "secretpassword" );
100 | }
101 |
102 | private void testCanBackupAndRestore(boolean asDefaultUser, String password) throws Exception
103 | {
104 | final String dbUser = "neo4j";
105 |
106 | // BACKUP
107 | // start a database and populate data
108 | GenericContainer neo4j = createDBContainer( asDefaultUser, password );
109 | Path dataDir = temporaryFolderManager.createFolderAndMountAsVolume(neo4j, "/data");
110 | neo4j.start();
111 | DatabaseIO dbio = new DatabaseIO( neo4j );
112 | dbio.putInitialDataIntoContainer( dbUser, password );
113 | dbio.verifyInitialDataInContainer( dbUser, password );
114 |
115 | // start admin container to initiate backup
116 | String neoDBAddress = neo4j.getHost()+":"+neo4j.getMappedPort( 6362 );
117 | GenericContainer adminBackup = createAdminContainer( asDefaultUser )
118 | .withNetworkMode( "host" )
119 | .waitingFor( new LogMessageWaitStrategy().withRegEx( "^Backup complete successful.*" ) )
120 | .withCommand( "neo4j-admin", "backup", "--database=neo4j", "--backup-dir=/backups", "--from="+neoDBAddress);
121 |
122 | Path backupDir = temporaryFolderManager.createFolderAndMountAsVolume(adminBackup, "/backups");
123 | adminBackup.start();
124 |
125 | Assertions.assertTrue( neo4j.isRunning(), "neo4j container should still be running" );
126 | dbio.verifyInitialDataInContainer( dbUser, password );
127 | adminBackup.stop();
128 |
129 | // RESTORE
130 |
131 | // write more stuff
132 | dbio.putMoreDataIntoContainer( dbUser, password );
133 | dbio.verifyMoreDataIntoContainer( dbUser, password, true );
134 |
135 | // do restore
136 | dbio.runCypherQuery( dbUser, password, "STOP DATABASE neo4j", "system" );
137 | GenericContainer adminRestore = createAdminContainer( asDefaultUser )
138 | .waitingFor( new LogMessageWaitStrategy().withRegEx( "^.*restoreStatus=successful.*" ) )
139 | .withCommand( "neo4j-admin", "restore", "--database=neo4j", "--from=/backups/neo4j", "--force");
140 | temporaryFolderManager.mountHostFolderAsVolume( adminRestore, backupDir, "/backups" );
141 | temporaryFolderManager.mountHostFolderAsVolume( adminRestore, dataDir, "/data" );
142 | adminRestore.start();
143 | dbio.runCypherQuery( dbUser, password, "START DATABASE neo4j", "system" );
144 |
145 | // verify new stuff is missing
146 | dbio.verifyMoreDataIntoContainer( dbUser, password, false );
147 |
148 | // clean up
149 | adminRestore.stop();
150 | neo4j.stop();
151 | }
152 | }
153 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/src/test/java/com/neo4j/docker/utils/DatabaseIO.java:
--------------------------------------------------------------------------------
```java
1 | package com.neo4j.docker.utils;
2 |
3 | import com.neo4j.docker.coredb.configurations.Configuration;
4 | import org.junit.jupiter.api.Assertions;
5 | import org.neo4j.driver.AuthToken;
6 | import org.neo4j.driver.AuthTokens;
7 | import org.neo4j.driver.Config;
8 | import org.neo4j.driver.Driver;
9 | import org.neo4j.driver.GraphDatabase;
10 | import org.neo4j.driver.Record;
11 | import org.neo4j.driver.Result;
12 | import org.neo4j.driver.Session;
13 | import org.neo4j.driver.SessionConfig;
14 | import org.slf4j.Logger;
15 | import org.slf4j.LoggerFactory;
16 | import org.testcontainers.containers.GenericContainer;
17 |
18 | import java.util.List;
19 | import java.util.stream.Collectors;
20 |
21 | public class DatabaseIO
22 | {
23 | private static final Config DEFAULT_DRIVER_CONFIG = Config.builder().withoutEncryption().build();
24 | private final Logger log = LoggerFactory.getLogger( DatabaseIO.class );
25 |
26 | private GenericContainer container;
27 | private String boltUri;
28 |
29 | public DatabaseIO( GenericContainer container )
30 | {
31 | this.container = container;
32 | this.boltUri = "bolt://"+container.getHost()+":"+container.getMappedPort( 7687 );
33 | }
34 |
35 | public DatabaseIO( String host, Integer boltPort )
36 | {
37 | this.boltUri = "bolt://" + host + ":" + boltPort;
38 | }
39 |
40 | public void putInitialDataIntoContainer( String user, String password )
41 | {
42 | log.info( "Writing data into database" );
43 | List<Record> result = runCypherQuery( user, password,"CREATE (arne:dog {name:'Arne'})-[:SNIFFS]->(bosse:dog {name:'Bosse'}) RETURN arne.name" );
44 | Assertions.assertEquals( "Arne", result.get( 0 ).get( "arne.name" ).asString(), "did not receive expected result from cypher CREATE query" );
45 | }
46 |
47 | public void verifyInitialDataInContainer( String user, String password )
48 | {
49 | log.info( "verifying data is present in the database" );
50 | List<Record> result = runCypherQuery( user, password,"MATCH (a:dog)-[:SNIFFS]->(b:dog) RETURN a.name");
51 | Assertions.assertEquals( "Arne", result.get( 0 ).get("a.name").asString(), "did not receive expected result from cypher MATCH query" );
52 | }
53 |
54 | public void putMoreDataIntoContainer( String user, String password )
55 | {
56 | log.info( "Writing more data into database" );
57 | List<Record> result = runCypherQuery( user, password,
58 | "MATCH (a:dog {name:'Arne'}) CREATE (armstrong:dog {name:'Armstrong'})-[:SNIFFS]->(a) return a.name, armstrong.name" );
59 | Assertions.assertEquals( "Arne", result.get( 0 ).get("a.name").asString(),
60 | "did not receive expected result from cypher MATCH query" );
61 | Assertions.assertEquals( "Armstrong", result.get( 0 ).get( "armstrong.name" ).asString(),
62 | "did not receive expected result from cypher CREATE query" );
63 | }
64 |
65 | public void verifyMoreDataIntoContainer( String user, String password, boolean extraDataShouldBeThere )
66 | {
67 | log.info( "Verifying extra data is {}in database", extraDataShouldBeThere? "":"not " );
68 | List<Record> result = runCypherQuery( user, password,"MATCH (a:dog)-[:SNIFFS]->(b:dog) RETURN a.name");
69 | String dogs = result.stream()
70 | .map( record -> record.get( 0 ).asString() )
71 | .sorted()
72 | .collect( Collectors.joining(","));
73 | // dogs should now be a String which is a comma delimited list of dog names
74 |
75 | if(extraDataShouldBeThere)
76 | {
77 | Assertions.assertEquals( "Armstrong,Arne", dogs, "cypher query did not return correct data" );
78 | }
79 | else
80 | {
81 | Assertions.assertEquals( "Arne", dogs, "cypher query did not return correct data" );
82 | }
83 | }
84 |
85 | public String getConfigurationSettingAsString( String user, String password, Configuration conf)
86 | {
87 | List<Record> confRecord = runCypherQuery( user, password,
88 | "CALL dbms.listConfig() YIELD name, value " +
89 | "WHERE name='" + conf.name + "' " +
90 | "RETURN value" );
91 | Assertions.assertEquals(1, confRecord.size(), "Configuration "+conf.name+" was not set." );
92 | return confRecord.get( 0 ).get( 0 ).asString();
93 | }
94 |
95 | public void verifyConfigurationSetting( String user, String password, Configuration conf, String expectedValue)
96 | {
97 | verifyConfigurationSetting(user, password, conf, expectedValue, "");
98 | }
99 |
100 | public void verifyConfigurationSetting( String user, String password, Configuration conf, String expectedValue, String extraFailureMsg)
101 | {
102 | String actualConf = getConfigurationSettingAsString( user, password, conf );
103 | Assertions.assertEquals(expectedValue, actualConf,
104 | String.format("Expected %s to be %s but it was %s.%s",
105 | conf.name, expectedValue, actualConf, extraFailureMsg));
106 | }
107 |
108 | public void changePassword(String user, String oldPassword, String newPassword)
109 | {
110 | if(TestSettings.NEO4J_VERSION.isAtLeastVersion( Neo4jVersion.NEO4J_VERSION_400 ))
111 | {
112 | String cypher = "ALTER CURRENT USER SET PASSWORD FROM '"+oldPassword+"' TO '"+newPassword+"'";
113 | runCypherQuery( user, oldPassword, cypher, "system" );
114 | }
115 | else
116 | {
117 | runCypherQuery( user, oldPassword, "CALL dbms.changePassword('"+newPassword+"')" );
118 | }
119 | }
120 |
121 | public List<Record> runCypherQuery( String user, String password, String cypher)
122 | {
123 | // we don't just do runCypherQuery( user, password, cypher, "neo4j")
124 | // because it breaks the upgrade tests from 3.5.x
125 | List<Record> records;
126 | Driver driver = GraphDatabase.driver( boltUri, getToken( user, password ), DEFAULT_DRIVER_CONFIG);
127 | try ( Session session = driver.session())
128 | {
129 | Result rs = session.run( cypher );
130 | records = rs.list();
131 | }
132 | driver.close();
133 | return records;
134 | }
135 |
136 | public List<Record> runCypherQuery( String user, String password, String cypher, String database)
137 | {
138 | List<Record> records;
139 | Driver driver = GraphDatabase.driver( boltUri, getToken( user, password ), DEFAULT_DRIVER_CONFIG);
140 | try ( Session session = driver.session(SessionConfig.forDatabase( database )))
141 | {
142 | Result rs = session.run( cypher );
143 | records = rs.list();
144 | }
145 | driver.close();
146 | return records;
147 | }
148 |
149 | public void verifyConnectivity( String user, String password )
150 | {
151 | GraphDatabase.driver( boltUri,
152 | getToken( user, password ),
153 | DEFAULT_DRIVER_CONFIG)
154 | .verifyConnectivity();
155 | }
156 |
157 | private AuthToken getToken(String user, String password)
158 | {
159 | if(password.equals( "none" ))
160 | {
161 | return AuthTokens.none();
162 | }
163 | else
164 | {
165 | return AuthTokens.basic( user, password );
166 | }
167 | }
168 | }
169 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/src/test/java/com/neo4j/docker/coredb/configurations/TestJVMAdditionalConfig.java:
--------------------------------------------------------------------------------
```java
1 | package com.neo4j.docker.coredb.configurations;
2 |
3 | import com.neo4j.docker.utils.DatabaseIO;
4 | import com.neo4j.docker.utils.Neo4jVersion;
5 | import com.neo4j.docker.utils.SetContainerUser;
6 | import com.neo4j.docker.utils.TemporaryFolderManager;
7 | import com.neo4j.docker.utils.TestSettings;
8 | import com.neo4j.docker.utils.WaitStrategies;
9 | import org.junit.jupiter.api.Assertions;
10 | import org.junit.jupiter.api.Assumptions;
11 | import org.junit.jupiter.api.BeforeAll;
12 | import org.junit.jupiter.api.Test;
13 | import org.junit.jupiter.api.extension.RegisterExtension;
14 | import org.slf4j.Logger;
15 | import org.slf4j.LoggerFactory;
16 | import org.testcontainers.containers.GenericContainer;
17 | import org.testcontainers.containers.output.Slf4jLogConsumer;
18 |
19 | import java.nio.file.Files;
20 | import java.nio.file.Path;
21 |
22 | public class TestJVMAdditionalConfig
23 | {
24 | private final Logger log = LoggerFactory.getLogger( TestJVMAdditionalConfig.class );
25 | private static final String PASSWORD = "SuperSecretPassword";
26 | private static final String AUTH = "neo4j/"+PASSWORD ;
27 | private static Path confFolder;
28 | private static final Configuration JVM_ADDITIONAL_CONFIG = Configuration.getConfigurationNameMap().get( Setting.JVM_ADDITIONAL );
29 | private static final String DEFAULT_JVM_CONF = "-XX:+UseG1GC";
30 |
31 | @RegisterExtension
32 | public static TemporaryFolderManager temporaryFolderManager = new TemporaryFolderManager();
33 |
34 | @BeforeAll
35 | static void getVersionSpecificConfigurationSettings()
36 | {
37 | confFolder = Configuration.getConfigurationResourcesFolder();
38 | Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( Neo4jVersion.NEO4J_VERSION_440 ),
39 | "JVM Additional tests not applicable before 4.4.0");
40 | }
41 |
42 | private GenericContainer createContainer()
43 | {
44 | return new GenericContainer(TestSettings.IMAGE_ID)
45 | .withEnv("NEO4J_AUTH", AUTH)
46 | .withEnv("NEO4J_DEBUG", AUTH)
47 | .withEnv("NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes")
48 | .withExposedPorts(7474, 7687)
49 | .withLogConsumer(new Slf4jLogConsumer( log))
50 | .waitingFor(WaitStrategies.waitForNeo4jReady(PASSWORD));
51 | }
52 |
53 | @Test
54 | void testJvmAdditionalNotOverridden_noEnv() throws Exception
55 | {
56 | String expectedJvmAdditional = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005";
57 | testJvmAdditionalNotOverridden(expectedJvmAdditional, "" );
58 | }
59 |
60 | @Test
61 | void testJvmAdditionalNotOverridden_withEnv() throws Exception
62 | {
63 | String jvmAdditionalFromEnv = "-XX:+HeapDumpOnOutOfMemoryError";
64 | String expectedJvmAdditional = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005\n"+jvmAdditionalFromEnv;
65 | testJvmAdditionalNotOverridden(expectedJvmAdditional, jvmAdditionalFromEnv );
66 | }
67 |
68 | void testJvmAdditionalNotOverridden( String expectedJvmAdditional, String jvmAdditionalEnv ) throws Exception
69 | {
70 | try( GenericContainer container = createContainer())
71 | {
72 | //Mount /conf
73 | Path confMount = temporaryFolderManager.createFolderAndMountAsVolume(container, "/conf");
74 | SetContainerUser.nonRootUser( container );
75 | container.withEnv( JVM_ADDITIONAL_CONFIG.envName, jvmAdditionalEnv );
76 | //Create JvmAdditionalNotOverridden.conf file
77 | Path confFile = confFolder.resolve( "JvmAdditionalNotOverridden.conf" );
78 | Files.copy( confFile, confMount.resolve( "neo4j.conf" ) );
79 | //Start the container
80 | container.start();
81 | // verify setting correctly loaded into neo4j
82 | DatabaseIO dbio = new DatabaseIO( container );
83 | dbio.verifyConfigurationSetting( "neo4j", PASSWORD, JVM_ADDITIONAL_CONFIG, expectedJvmAdditional);
84 | }
85 | }
86 |
87 | @Test
88 | void testJVMAdditionalDefaultsNotOverwrittenByEnv() throws Exception
89 | {
90 | String expectedJvmAdditional = "-XX:+HeapDumpOnOutOfMemoryError";
91 | try( GenericContainer container = createContainer())
92 | {
93 | container.withEnv( JVM_ADDITIONAL_CONFIG.envName, expectedJvmAdditional );
94 | verifyJvmAdditional( container, expectedJvmAdditional, DEFAULT_JVM_CONF );
95 | }
96 | }
97 |
98 | @Test
99 | void testSpecialCharInJvmAdditional_space_conf() throws Exception
100 | {
101 | testJvmAdditionalSpecialCharacters_conf("space", "-XX:OnOutOfMemoryError=\"/usr/bin/echo oh no oom\"");
102 | }
103 |
104 | @Test
105 | void testSpecialCharInJvmAdditional_space_env() throws Exception
106 | {
107 | testJvmAdditionalSpecialCharacters_env( "-XX:OnOutOfMemoryError=\"/usr/bin/echo oh no oom\"");
108 | }
109 |
110 | @Test
111 | void testSpecialCharInJvmAdditional_dollar_conf() throws Exception
112 | {
113 | testJvmAdditionalSpecialCharacters_conf("dollar",
114 | "-Dnot.a.real.parameter=\"beepbeep$boop1boop2\"" );
115 | }
116 |
117 | @Test
118 | void testSpecialCharInJvmAdditional_dollar_env() throws Exception {
119 | testJvmAdditionalSpecialCharacters_env( "-Dnot.a.real.parameter=\"bleepblorp$bleep1blorp4\"");
120 | }
121 |
122 | void testJvmAdditionalSpecialCharacters_conf(String charName, String expectedJvmAdditional) throws Exception
123 | {
124 | try(GenericContainer container = createContainer())
125 | {
126 | //Mount /conf
127 | Path confMount = temporaryFolderManager.createFolderAndMountAsVolume(container, "/conf");
128 | //copy test conf file
129 | String confContent = JVM_ADDITIONAL_CONFIG.name + "=" + expectedJvmAdditional;
130 | Files.write( confMount.resolve( "neo4j.conf" ), confContent.getBytes() );
131 | //Start the container
132 | verifyJvmAdditional( container, expectedJvmAdditional );
133 | }
134 | }
135 |
136 | void testJvmAdditionalSpecialCharacters_env( String expectedJvmAdditional ) throws Exception
137 | {
138 | try(GenericContainer container = createContainer())
139 | {
140 | container.withEnv( JVM_ADDITIONAL_CONFIG.envName, expectedJvmAdditional);
141 | verifyJvmAdditional( container, expectedJvmAdditional, DEFAULT_JVM_CONF );
142 | }
143 | }
144 |
145 | void verifyJvmAdditional( GenericContainer container, String... expectedValues )
146 | {
147 | SetContainerUser.nonRootUser( container );
148 | //Start the container
149 | container.start();
150 | // verify setting correctly loaded into neo4j
151 | DatabaseIO dbio = new DatabaseIO( container );
152 | String actualConfValue = dbio.getConfigurationSettingAsString( "neo4j", PASSWORD, JVM_ADDITIONAL_CONFIG );
153 |
154 | for(String expectedJvmAdditional : expectedValues)
155 | {
156 | Assertions.assertTrue( actualConfValue.contains( expectedJvmAdditional ) );
157 | }
158 | }
159 | }
160 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/src/test/java/com/neo4j/docker/coredb/plugins/StubPluginHelper.java:
--------------------------------------------------------------------------------
```java
1 | package com.neo4j.docker.coredb.plugins;
2 |
3 | import com.google.gson.Gson;
4 | import com.neo4j.docker.utils.DatabaseIO;
5 | import com.neo4j.docker.utils.HostFileHttpHandler;
6 | import com.neo4j.docker.utils.HttpServerTestExtension;
7 | import com.neo4j.docker.utils.Neo4jVersion;
8 | import org.junit.jupiter.api.Assertions;
9 | import org.neo4j.driver.Record;
10 |
11 | import java.io.File;
12 | import java.io.IOException;
13 | import java.net.URISyntaxException;
14 | import java.nio.file.Path;
15 | import java.util.Collections;
16 | import java.util.List;
17 | import java.util.Map;
18 | import java.util.stream.Collectors;
19 |
20 | import static com.neo4j.docker.utils.TestSettings.NEO4J_VERSION;
21 |
22 | public class StubPluginHelper
23 | {
24 | public static final String PLUGIN_FILENAME = "myPlugin.jar";
25 | public static final String PLUGIN_ENV_NAME = "_testing";
26 | private final HttpServerTestExtension httpServer;
27 |
28 | /**Data class for each <code>versions.json</code> entry so that the GSON tool can convert it to json.
29 | * */
30 | private class VersionsJsonEntry
31 | {
32 | String neo4j;
33 | String jar;
34 | String _testing;
35 |
36 | VersionsJsonEntry( String neo4j, String jar )
37 | {
38 | this.neo4j = neo4j;
39 | this._testing = "SNAPSHOT";
40 | this.jar = "http://host.testcontainers.internal:3000/" + jar;
41 | }
42 | }
43 |
44 | /**Does (most of) the complicated setup required to create a fake neo4j plugin and make it accessible inside the container.
45 | * @param httpServer a {@link HttpServerTestExtension} object.
46 | * It must have ALREADY been registered as a JUnit5 extension with {@link org.junit.jupiter.api.extension.RegisterExtension}
47 | * */
48 | public StubPluginHelper(HttpServerTestExtension httpServer)
49 | {
50 | this.httpServer = httpServer;
51 | }
52 |
53 | /**Creates a versions.json in the destination folder mapping between the given neo4j versions and jar filenames.
54 | * This is currently used to map between neo4j versions and jars that don't exist (for semver match testing).
55 | * @param destinationFolder folder to save versions.json to.
56 | * @param version a neo4j version, to map to the real testing jar.
57 | * @return File object of the versions.json file created.
58 | * */
59 | public File createStubPluginForVersion(Path destinationFolder, Neo4jVersion version) throws IOException
60 | {
61 | return createStubPluginsForVersionMapping(destinationFolder, Collections.singletonMap(version.toString(), PLUGIN_FILENAME));
62 | }
63 |
64 | /**Creates a versions.json in the destination folder mapping between the given neo4j versions and jar filenames.
65 | * This is currently used to map between neo4j versions and jars that don't exist (for semver match testing).
66 | * @param destinationFolder folder to save versions.json to.
67 | * @param version a neo4j version, as a string, to map to the real testing jar.
68 | * @return File object of the versions.json file created.
69 | * */
70 | public File createStubPluginForVersion(Path destinationFolder, String version) throws IOException
71 | {
72 | return createStubPluginsForVersionMapping(destinationFolder, Collections.singletonMap(version, PLUGIN_FILENAME));
73 | }
74 |
75 | /**Creates a versions.json in the destination folder mapping between the given neo4j versions and jar filenames.
76 | * This is currently used to map between neo4j versions and jars that don't exist (for semver match testing).
77 | * @param destinationFolder folder to save versions.json to.
78 | * @param versionAndJar map of neo4j version, as a string, to jar name. For example:
79 | * 4.4.10 -> myPlugin.jar
80 | * 4.4.* -> pluginThatDoesNotExist.jar
81 | * 5.0.x -> anotherNonexistantPlugin.jar
82 | * @return File object of the versions.json file created.
83 | * */
84 | public File createStubPluginsForVersionMapping(Path destinationFolder, Map<String,String> versionAndJar ) throws IOException
85 | {
86 | File versionsJson = createVersionsJson(destinationFolder, versionAndJar);
87 | try {
88 | File myPluginJar = new File(getClass().getClassLoader().getResource("stubplugin/" + PLUGIN_FILENAME).toURI());
89 |
90 | httpServer.registerHandler(versionsJson.getName(), new HostFileHttpHandler(versionsJson, "application/json"));
91 | httpServer.registerHandler(PLUGIN_FILENAME, new HostFileHttpHandler(myPluginJar, "application/java-archive"));
92 | }
93 | catch (URISyntaxException e)
94 | {
95 | throw new IOException("Could not load test plugin from test resources file", e);
96 | }
97 | return versionsJson;
98 | }
99 |
100 | private File createVersionsJson(Path destinationFolder, Map<String, String> versionAndJar) throws IOException
101 | {
102 | List<VersionsJsonEntry> jsonEntries = versionAndJar.keySet()
103 | .stream()
104 | .map(key -> new VersionsJsonEntry(key, versionAndJar.get(key)))
105 | .collect(Collectors.toList());
106 | Gson jsonBuilder = new Gson();
107 | String jsonStr = jsonBuilder.toJson(jsonEntries);
108 |
109 | File outputJsonFile = destinationFolder.resolve("versions.json").toFile();
110 | java.nio.file.Files.writeString(outputJsonFile.toPath(), jsonStr);
111 | return outputJsonFile;
112 | }
113 |
114 | public void verifyStubPluginLoaded(DatabaseIO db, String user, String password )
115 | {
116 | // when we check the list of installed procedures...
117 | String listProceduresCypherQuery = NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 4, 3, 0 ) ) ?
118 | "SHOW PROCEDURES YIELD name, signature RETURN name, signature" :
119 | "CALL dbms.procedures() YIELD name, signature RETURN name, signature";
120 | List<Record> procedures = db.runCypherQuery( user, password, listProceduresCypherQuery );
121 | // Then the procedure from the test plugin should be listed
122 | Assertions.assertTrue( procedures.stream()
123 | .anyMatch( x -> x.get( "name" ).asString()
124 | .equals( "com.neo4j.docker.test.myplugin.defaultValues" ) ),
125 | "Missing procedure provided by our plugin" );
126 |
127 | // When we call the procedure from the plugin
128 | List<Record> pluginResponse = db.runCypherQuery( user, password,
129 | "CALL com.neo4j.docker.test.myplugin.defaultValues" );
130 |
131 | // Then we get the response we expect
132 | Assertions.assertEquals( 1, pluginResponse.size(), "Our procedure should only return a single result" );
133 | Record record = pluginResponse.get( 0 );
134 |
135 | String message = "Result from calling our procedure doesnt match our expectations";
136 | Assertions.assertEquals( "a string", record.get( "string" ).asString(), message );
137 | Assertions.assertEquals( 42L, record.get( "integer" ).asInt(), message );
138 | Assertions.assertEquals( 3.14d, record.get( "aFloat" ).asDouble(), 0.000001, message );
139 | Assertions.assertTrue(record.get("aBoolean").asBoolean(), message);
140 | }
141 | }
142 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/src/test/java/com/neo4j/docker/neo4jadmin/TestDumpLoad44.java:
--------------------------------------------------------------------------------
```java
1 | package com.neo4j.docker.neo4jadmin;
2 |
3 | import com.github.dockerjava.api.command.CreateContainerCmd;
4 | import com.neo4j.docker.utils.DatabaseIO;
5 | import com.neo4j.docker.utils.Neo4jVersion;
6 | import com.neo4j.docker.utils.SetContainerUser;
7 | import com.neo4j.docker.utils.WaitStrategies;
8 | import com.neo4j.docker.utils.TemporaryFolderManager;
9 | import com.neo4j.docker.utils.TestSettings;
10 | import org.junit.jupiter.api.Assertions;
11 | import org.junit.jupiter.api.Assumptions;
12 | import org.junit.jupiter.api.BeforeAll;
13 | import org.junit.jupiter.api.Test;
14 | import org.junit.jupiter.api.extension.RegisterExtension;
15 | import org.slf4j.Logger;
16 | import org.slf4j.LoggerFactory;
17 | import org.testcontainers.containers.GenericContainer;
18 | import org.testcontainers.containers.output.Slf4jLogConsumer;
19 | import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy;
20 | import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
21 |
22 | import java.nio.file.Path;
23 | import java.time.Duration;
24 | import java.util.function.Consumer;
25 |
26 | public class TestDumpLoad44
27 | {
28 | private static Logger log = LoggerFactory.getLogger( TestDumpLoad44.class );
29 | @RegisterExtension
30 | public static TemporaryFolderManager temporaryFolderManager = new TemporaryFolderManager();
31 |
32 | @BeforeAll
33 | static void beforeAll()
34 | {
35 | Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( new Neo4jVersion( 4, 4, 0 )),
36 | "Neo4j admin image not available before 4.4.0");
37 | Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isOlderThan( Neo4jVersion.NEO4J_VERSION_500 ),
38 | "These Neo4j admin tests are only for 4.4");
39 | }
40 |
41 | private GenericContainer createDBContainer( boolean asDefaultUser, String password )
42 | {
43 | String auth = "none";
44 | if(!password.equalsIgnoreCase("none"))
45 | {
46 | auth = "neo4j/"+password;
47 | }
48 |
49 | GenericContainer container = new GenericContainer( TestSettings.IMAGE_ID );
50 | container.withEnv( "NEO4J_AUTH", auth )
51 | .withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
52 | .withExposedPorts( 7474, 7687 )
53 | .withLogConsumer( new Slf4jLogConsumer( log ) )
54 | .waitingFor( WaitStrategies.waitForNeo4jReady( password ) )
55 | // the default testcontainer framework behaviour is to just stop the process entirely,
56 | // preventing clean shutdown. This means we can run the stop command and
57 | // it'll send a SIGTERM to initiate neo4j shutdown. See also stopContainer method.
58 | .withCreateContainerCmdModifier(
59 | (Consumer<CreateContainerCmd>) cmd -> cmd.withStopSignal( "SIGTERM" ).withStopTimeout( 20 ));
60 | if(!asDefaultUser)
61 | {
62 | SetContainerUser.nonRootUser( container );
63 | }
64 | return container;
65 | }
66 |
67 | private GenericContainer createAdminContainer( boolean asDefaultUser )
68 | {
69 | GenericContainer container = new GenericContainer( TestSettings.ADMIN_IMAGE_ID );
70 | container.withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
71 | .withExposedPorts( 7474, 7687 )
72 | .withLogConsumer( new Slf4jLogConsumer( log ) )
73 | .waitingFor( new LogMessageWaitStrategy().withRegEx( "^Done: \\d+ files, [\\d\\.,]+[KMGi]*B processed.*" ) )
74 | .withStartupCheckStrategy( new OneShotStartupCheckStrategy().withTimeout( Duration.ofSeconds( 90 ) ) );
75 | if(!asDefaultUser)
76 | {
77 | SetContainerUser.nonRootUser( container );
78 | }
79 | return container;
80 | }
81 |
82 | @Test
83 | void shouldDumpAndLoad_defaultUser_noAuth() throws Exception
84 | {
85 | shouldCreateDumpAndLoadDump( true, "none" );
86 | }
87 |
88 | @Test
89 | void shouldDumpAndLoad_nonDefaultUser_noAuth() throws Exception
90 | {
91 | shouldCreateDumpAndLoadDump( false, "none" );
92 | }
93 |
94 | @Test
95 | void shouldDumpAndLoad_defaultUser_withAuth() throws Exception
96 | {
97 | shouldCreateDumpAndLoadDump( true, "verysecretpassword" );
98 | }
99 |
100 | @Test
101 | void shouldDumpAndLoad_nonDefaultUser_withAuth() throws Exception
102 | {
103 | shouldCreateDumpAndLoadDump( false, "verysecretpassword" );
104 | }
105 |
106 | //container.stop() actually runs the killContainer Command, preventing clean shutdown.
107 | // This runs the actual stop command. Which we set up in createDBContainer to send SIGTERM
108 | private void stopContainer(GenericContainer container)
109 | {
110 | log.info( "issuing container stop command" );
111 | container.getDockerClient().stopContainerCmd( container.getContainerId() ).exec();
112 | log.info( "Container stopped" );
113 | }
114 |
115 | private void shouldCreateDumpAndLoadDump( boolean asDefaultUser, String password ) throws Exception
116 | {
117 | Path firstDataDir;
118 | Path secondDataDir;
119 | Path backupDir;
120 |
121 | // start a database and populate it
122 | try(GenericContainer container = createDBContainer( asDefaultUser, password ))
123 | {
124 | firstDataDir = temporaryFolderManager.createNamedFolderAndMountAsVolume( container,
125 | "data1",
126 | "/data" );
127 | container.start();
128 | DatabaseIO dbio = new DatabaseIO( container );
129 | dbio.putInitialDataIntoContainer( "neo4j", password );
130 | stopContainer( container );
131 | }
132 |
133 | // use admin container to create dump
134 | try(GenericContainer admin = createAdminContainer( asDefaultUser ))
135 | {
136 | temporaryFolderManager.mountHostFolderAsVolume( admin, firstDataDir, "/data" );
137 | backupDir = temporaryFolderManager.createFolderAndMountAsVolume(admin, "/backups");
138 | admin.withCommand( "neo4j-admin", "dump", "--database=neo4j", "--to=/backups/neo4j.dump" );
139 | admin.start();
140 | }
141 | Assertions.assertTrue( backupDir.resolve( "neo4j.dump" ).toFile().exists(), "dump file not created");
142 |
143 | // dump file exists. Now try to load it into a new database.
144 | // use admin container to create dump
145 | try(GenericContainer admin = createAdminContainer( asDefaultUser ))
146 | {
147 | secondDataDir = temporaryFolderManager.createNamedFolderAndMountAsVolume( admin,
148 | "data2",
149 | "/data" );
150 | temporaryFolderManager.mountHostFolderAsVolume( admin, backupDir, "/backups" );
151 | admin.withCommand( "neo4j-admin", "load", "--database=neo4j", "--from=/backups/neo4j.dump" );
152 | admin.start();
153 | }
154 |
155 | // verify data in 2nd data directory by starting a database and verifying data we populated earlier
156 | try(GenericContainer container = createDBContainer( asDefaultUser, password ))
157 | {
158 | temporaryFolderManager.mountHostFolderAsVolume( container, secondDataDir, "/data" );
159 | container.start();
160 | DatabaseIO dbio = new DatabaseIO( container );
161 | dbio.verifyInitialDataInContainer( "neo4j", password );
162 | }
163 | }
164 | }
165 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/build-docker-image.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash
2 | set -eu -o pipefail
3 |
4 | # SUPPORTED_IMAGE_OS=("debian" "ubi9")
5 | EDITIONS=("community" "enterprise")
6 |
7 | DISTRIBUTION_SITE="https://dist.neo4j.org"
8 | ROOT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
9 | source "$ROOT_DIR/build-utils-common-functions.sh"
10 | BUILD_DIR=${ROOT_DIR}/build
11 | SRC_DIR=${ROOT_DIR}/docker-image-src
12 | # shellcheck disable=SC2034 # Used in docker-common-functions.sh
13 | TAR_CACHE=${ROOT_DIR}/in
14 |
15 | function usage
16 | {
17 | echo >&2 "USAGE: $0 <version> <edition> <operating system>
18 | For example:
19 | $0 4.4.10 community debian
20 | $0 5.10.0 enterprise ubi9
21 | Version and operating system can also be set in the environment.
22 | For example:
23 | NEO4JVERSION=4.4.10 NEO4JEDITION=community IMAGE_OS=debian $0
24 | NEO4JVERSION=5.10.0 NEO4JEDITION=enterprise IMAGE_OS=ubi9 $0
25 | "
26 | exit 1
27 | }
28 |
29 | ## ==========================================
30 | ## get and sanitise script inputs
31 |
32 | if [[ $# -eq 3 ]]; then
33 | NEO4JVERSION=${1}
34 | NEO4JEDITION=${2}
35 | IMAGE_OS=${3}
36 | elif [[ -z ${NEO4JVERSION:-""} ]]; then
37 | echo >&2 "NEO4JVERSION is unset. Either set it in the environment or pass as argument to this script."
38 | usage
39 | elif [[ -z ${NEO4JEDITION:-""} ]]; then
40 | echo >&2 "NEO4JEDITION is unset. Either set it in the environment or pass as argument to this script."
41 | usage
42 | elif [[ -z ${IMAGE_OS:-""} ]]; then
43 | echo >&2 "IMAGE_OS is unset. Either set it in the environment or pass as argument to this script."
44 | usage
45 | fi
46 | # verify edition
47 | if ! contains_element "${NEO4JEDITION}" "${EDITIONS[@]}"; then
48 | echo >&2 "${NEO4JEDITION} is not a supported edition."
49 | usage
50 | fi
51 | # verify compatible neo4j version
52 | if [[ ! "${NEO4JVERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then
53 | echo "\"${NEO4JVERSION}\" is not a valid version number."
54 | usage
55 | fi
56 |
57 | # get source files
58 | BRANCH=$(get_branch_from_version ${NEO4JVERSION})
59 | DOCKERFILE_NAME=$(get_compatible_dockerfile_for_os_or_error "${BRANCH}" "${IMAGE_OS}")
60 |
61 | echo "Building docker neo4j-${NEO4JEDITION}-${NEO4JVERSION} image based on ${IMAGE_OS}."
62 |
63 | ## ==================================================================================
64 | ## get neo4j source tar from distribution site. This is required for release artifacts
65 | ## so that we can calculate the sha256, and for the local image build.
66 |
67 | echo "Caching neo4j source tarball"
68 | fetch_tarball "${NEO4JVERSION}" "${NEO4JEDITION}"
69 |
70 | ## ==================================================================================
71 | ## construct local build context. These are all the files required to build the
72 | ## neo4j image locally.
73 |
74 | echo "Building local context for docker build"
75 | COREDB_LOCALCXT_DIR=${BUILD_DIR}/${IMAGE_OS}/coredb/${NEO4JEDITION}
76 | ADMIN_LOCALCXT_DIR=${BUILD_DIR}/${IMAGE_OS}/neo4j-admin/${NEO4JEDITION}
77 | mkdir -p ${COREDB_LOCALCXT_DIR}
78 | mkdir -p ${ADMIN_LOCALCXT_DIR}
79 |
80 | # copy coredb sources
81 | mkdir -p ${COREDB_LOCALCXT_DIR}/local-package
82 | cp ${SRC_DIR}/common/* ${COREDB_LOCALCXT_DIR}/local-package
83 | cp ${SRC_DIR}/${BRANCH}/coredb/*.sh ${COREDB_LOCALCXT_DIR}/local-package
84 | cp ${SRC_DIR}/${BRANCH}/coredb/*.json ${COREDB_LOCALCXT_DIR}/local-package
85 | coredb_sha=$(sha256sum "$(cached_tarball "${NEO4JVERSION}" "${NEO4JEDITION}")" | cut -d' ' -f1)
86 | cp "$(cached_tarball "${NEO4JVERSION}" "${NEO4JEDITION}")" ${COREDB_LOCALCXT_DIR}/local-package/
87 |
88 | # create coredb Dockerfile
89 | cp "${SRC_DIR}/${BRANCH}/coredb/${DOCKERFILE_NAME}" "${COREDB_LOCALCXT_DIR}/Dockerfile"
90 | sed -i -e "s|%%NEO4J_SHA%%|${coredb_sha}|" "${COREDB_LOCALCXT_DIR}/Dockerfile"
91 | sed -i -e "s|%%NEO4J_TARBALL%%|$(tarball_name "${NEO4JVERSION}" "${NEO4JEDITION}")|" "${COREDB_LOCALCXT_DIR}/Dockerfile"
92 | sed -i -e "s|%%NEO4J_EDITION%%|${NEO4JEDITION}|" "${COREDB_LOCALCXT_DIR}/Dockerfile"
93 | sed -i -e "s|%%NEO4J_DIST_SITE%%|${DISTRIBUTION_SITE}|" "${COREDB_LOCALCXT_DIR}/Dockerfile"
94 |
95 | # copy neo4j-admin sources
96 | mkdir -p ${ADMIN_LOCALCXT_DIR}/local-package
97 | cp ${SRC_DIR}/common/* ${ADMIN_LOCALCXT_DIR}/local-package
98 | cp "$(cached_tarball "${NEO4JVERSION}" "${NEO4JEDITION}")" ${ADMIN_LOCALCXT_DIR}/local-package/
99 | cp ${SRC_DIR}/${BRANCH}/neo4j-admin/*.sh ${ADMIN_LOCALCXT_DIR}/local-package
100 |
101 | # create neo4j-admin Dockerfile
102 | cp "${SRC_DIR}/${BRANCH}/neo4j-admin/${DOCKERFILE_NAME}" "${ADMIN_LOCALCXT_DIR}/Dockerfile"
103 | sed -i -e "s|%%NEO4J_SHA%%|${coredb_sha}|" "${ADMIN_LOCALCXT_DIR}/Dockerfile"
104 | sed -i -e "s|%%NEO4J_TARBALL%%|$(tarball_name ${NEO4JVERSION} ${NEO4JEDITION})|" "${ADMIN_LOCALCXT_DIR}/Dockerfile"
105 | sed -i -e "s|%%NEO4J_EDITION%%|${NEO4JEDITION}|" "${ADMIN_LOCALCXT_DIR}/Dockerfile"
106 | sed -i -e "s|%%NEO4J_DIST_SITE%%|${DISTRIBUTION_SITE}|" "${ADMIN_LOCALCXT_DIR}/Dockerfile"
107 |
108 | # add deprecation warning if needed
109 | if [ "${IMAGE_OS}" = "ubi8" ]; then
110 | dep_msg="echo \>\&2 \"\n=======================================================\n
111 | Neo4j Red Hat UBI8 images are deprecated in favour of Red Hat UBI9.\n
112 | Update your codebase to use Neo4j Docker image tags ending with -ubi9 instead of -ubi8.\n\n
113 | This is the last Neo4j image available on Red Hat UBI8.\n
114 | By continuing to use UBI8 tagged Neo4j images you will not get further updates, \n
115 | including new features and security fixes.\n\n
116 | This message can not be suppressed.\n
117 | =======================================================\n\"\n"
118 | sed -i -e "s/#%%DEPRECATION_WARNING_PLACEHOLDER%%/$(echo ${dep_msg} | sed -z 's/\n/\\n/g')/" "${COREDB_LOCALCXT_DIR}/local-package/docker-entrypoint.sh"
119 | sed -i -e "s/#%%DEPRECATION_WARNING_PLACEHOLDER%%/$(echo ${dep_msg} | sed -z 's/\n/\\n/g')/" "${ADMIN_LOCALCXT_DIR}/local-package/docker-entrypoint.sh"
120 | else
121 | sed -i -e '/#%%DEPRECATION_WARNING_PLACEHOLDER%%/d' "${COREDB_LOCALCXT_DIR}/local-package/docker-entrypoint.sh"
122 | sed -i -e '/#%%DEPRECATION_WARNING_PLACEHOLDER%%/d' "${ADMIN_LOCALCXT_DIR}/local-package/docker-entrypoint.sh"
123 | fi
124 |
125 | ## ==================================================================================
126 | ## Finally we are ready to do a docker build...
127 |
128 | # build coredb
129 | coredb_image_tag=neo4jtest:${RANDOM}
130 | echo "Building CoreDB docker image for neo4j-${NEO4JVERSION} ${NEO4JEDITION} on ${IMAGE_OS}."
131 | docker build --tag=${coredb_image_tag} \
132 | --build-arg="NEO4J_URI=file:///startup/$(tarball_name "${NEO4JVERSION}" "${NEO4JEDITION}")" \
133 | "${COREDB_LOCALCXT_DIR}"
134 | echo "Tagged CoreDB image ${coredb_image_tag}"
135 | echo -n "${coredb_image_tag}" > ${COREDB_LOCALCXT_DIR}/../.image-id-"${NEO4JEDITION}"
136 |
137 | # build neo4j-admin
138 | admin_image_tag=neo4jadmintest:${RANDOM}
139 | echo "Building neo4j-admin docker image for neo4j-admin-${NEO4JVERSION} ${NEO4JEDITION} on ${IMAGE_OS}."
140 | docker build --tag=${admin_image_tag} \
141 | --build-arg="NEO4J_URI=file:///startup/$(tarball_name "${NEO4JVERSION}" "${NEO4JEDITION}")" \
142 | "${ADMIN_LOCALCXT_DIR}"
143 | echo "Tagged neo4j-admin image ${admin_image_tag}"
144 | echo -n "${admin_image_tag}" > ${ADMIN_LOCALCXT_DIR}/../.image-id-"${NEO4JEDITION}"
145 |
146 | ## ==================================================================================
147 | # generate env files for local development
148 | {
149 | echo "NEO4JVERSION=${NEO4JVERSION}"
150 | echo "NEO4J_IMAGE=$(cat "${COREDB_LOCALCXT_DIR}"/../.image-id-"${NEO4JEDITION}")"
151 | echo "NEO4JADMIN_IMAGE=$(cat "${ADMIN_LOCALCXT_DIR}"/../.image-id-"${NEO4JEDITION}")"
152 | echo "NEO4J_EDITION=${NEO4JEDITION}"
153 | echo "BASE_OS=${IMAGE_OS}"
154 | echo "NEO4J_SKIP_MOUNTED_FOLDER_TARBALLING=true"
155 | } > ${BUILD_DIR}/${IMAGE_OS}/devenv-"${NEO4JEDITION}".env
156 | ln -f ${BUILD_DIR}/${IMAGE_OS}/devenv-"${NEO4JEDITION}".env ${BUILD_DIR}/devenv-"${NEO4JEDITION}".env
157 |
158 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/src/test/java/com/neo4j/docker/neo4jadmin/TestBackupRestore.java:
--------------------------------------------------------------------------------
```java
1 | package com.neo4j.docker.neo4jadmin;
2 |
3 | import com.neo4j.docker.coredb.configurations.Configuration;
4 | import com.neo4j.docker.coredb.configurations.Setting;
5 | import com.neo4j.docker.utils.DatabaseIO;
6 | import com.neo4j.docker.utils.Neo4jVersion;
7 | import com.neo4j.docker.utils.SetContainerUser;
8 | import com.neo4j.docker.utils.WaitStrategies;
9 | import com.neo4j.docker.utils.TemporaryFolderManager;
10 | import com.neo4j.docker.utils.TestSettings;
11 | import org.junit.jupiter.api.Assertions;
12 | import org.junit.jupiter.api.Assumptions;
13 | import org.junit.jupiter.api.BeforeAll;
14 | import org.junit.jupiter.api.Test;
15 | import org.junit.jupiter.api.extension.RegisterExtension;
16 | import org.slf4j.Logger;
17 | import org.slf4j.LoggerFactory;
18 | import org.testcontainers.containers.GenericContainer;
19 | import org.testcontainers.containers.output.Slf4jLogConsumer;
20 | import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
21 | import org.testcontainers.containers.wait.strategy.Wait;
22 |
23 | import java.io.File;
24 | import java.nio.file.Files;
25 | import java.nio.file.Path;
26 | import java.time.Duration;
27 | import java.util.List;
28 | import java.util.Map;
29 |
30 | public class TestBackupRestore
31 | {
32 | // with authentication
33 | // with non-default user
34 | private final Logger log = LoggerFactory.getLogger( TestBackupRestore.class );
35 | @RegisterExtension
36 | public static TemporaryFolderManager temporaryFolderManager = new TemporaryFolderManager();
37 |
38 | @BeforeAll
39 | static void beforeAll()
40 | {
41 | Assumptions.assumeTrue( TestSettings.NEO4J_VERSION.isAtLeastVersion( Neo4jVersion.NEO4J_VERSION_500 ),
42 | "These tests only apply to neo4j-admin images of 5.0 and greater");
43 | Assumptions.assumeTrue( TestSettings.EDITION == TestSettings.Edition.ENTERPRISE,
44 | "backup and restore only available in Neo4j Enterprise" );
45 | }
46 |
47 | private GenericContainer createDBContainer( boolean asDefaultUser, String password )
48 | {
49 | String auth = "none";
50 | if(!password.equalsIgnoreCase("none"))
51 | {
52 | auth = "neo4j/"+password;
53 | }
54 | Map<Setting,Configuration> confNames = Configuration.getConfigurationNameMap();
55 | GenericContainer container = new GenericContainer( TestSettings.IMAGE_ID );
56 | container.withEnv( "NEO4J_AUTH", auth )
57 | .withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
58 | .withEnv( confNames.get( Setting.BACKUP_ENABLED ).envName, "true" )
59 | .withEnv( confNames.get( Setting.BACKUP_LISTEN_ADDRESS ).envName, "0.0.0.0:6362" )
60 | .withExposedPorts( 7474, 7687, 6362 )
61 | .withLogConsumer( new Slf4jLogConsumer( log ) )
62 | .waitingFor(WaitStrategies.waitForNeo4jReady( password ));
63 | if(!asDefaultUser)
64 | {
65 | SetContainerUser.nonRootUser( container );
66 | }
67 | return container;
68 | }
69 |
70 | private GenericContainer createAdminContainer( boolean asDefaultUser )
71 | {
72 | GenericContainer container = new GenericContainer( TestSettings.ADMIN_IMAGE_ID );
73 | container.withEnv( "NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes" )
74 | .withLogConsumer( new Slf4jLogConsumer( log ) );
75 | WaitStrategies.waitUntilContainerFinished( container, Duration.ofSeconds( 180) );
76 | if(!asDefaultUser)
77 | {
78 | SetContainerUser.nonRootUser( container );
79 | }
80 | return container;
81 | }
82 |
83 | @Test
84 | void shouldBackupAndRestore_defaultUser_noAuth() throws Exception
85 | {
86 | testCanBackupAndRestore( true, "none" );
87 | }
88 | @Test
89 | void shouldBackupAndRestore_nonDefaultUser_noAuth() throws Exception
90 | {
91 | testCanBackupAndRestore( false, "none" );
92 | }
93 | @Test
94 | void shouldBackupAndRestore_defaultUser_withAuth() throws Exception
95 | {
96 | testCanBackupAndRestore( true, "secretpassword" );
97 | }
98 | @Test
99 | void shouldBackupAndRestore_nonDefaultUser_withAuth() throws Exception
100 | {
101 | testCanBackupAndRestore( false, "secretpassword" );
102 | }
103 |
104 | private void testCanBackupAndRestore(boolean asDefaultUser, String password) throws Exception
105 | {
106 | final String dbUser = "neo4j";
107 | Path backupDir;
108 |
109 | // BACKUP
110 | // start a database and populate data
111 | try(GenericContainer neo4j = createDBContainer( asDefaultUser, password ))
112 | {
113 | Path dataDir = temporaryFolderManager.createFolderAndMountAsVolume(neo4j, "/data");
114 | neo4j.start();
115 | DatabaseIO dbio = new DatabaseIO( neo4j );
116 | dbio.putInitialDataIntoContainer( dbUser, password );
117 | dbio.verifyInitialDataInContainer( dbUser, password );
118 |
119 | // start admin container to initiate backup
120 | String neoDBAddress = neo4j.getHost() + ":" + neo4j.getMappedPort( 6362 );
121 | try(GenericContainer adminBackup = createAdminContainer( asDefaultUser ))
122 | {
123 | adminBackup.withNetworkMode( "host" )
124 | .waitingFor( new LogMessageWaitStrategy().withRegEx( "^Backup command completed.*" ) )
125 | .withCommand( "neo4j-admin",
126 | "database",
127 | "backup",
128 | "--to-path=/backups",
129 | "--include-metadata=all",
130 | "--from=" + neoDBAddress,
131 | "neo4j" );
132 |
133 | backupDir = temporaryFolderManager.createFolderAndMountAsVolume(adminBackup, "/backups");
134 | adminBackup.start();
135 |
136 | Assertions.assertTrue( neo4j.isRunning(), "neo4j container should still be running" );
137 | dbio.verifyInitialDataInContainer( dbUser, password );
138 | } //adminBackup goes out of scope here
139 |
140 | // find backup file name and verify its existence.
141 | List<Path> backupFolder = Files.list( backupDir )
142 | .filter( p -> p.toFile().getName().startsWith( "neo4j" ) )
143 | .toList();
144 | Assertions.assertEquals( 1, backupFolder.size(), "No backup file was created" );
145 | File backupFile = backupFolder.get( 0 ).toFile();
146 |
147 |
148 | // RESTORE
149 |
150 | // write more stuff
151 | dbio.putMoreDataIntoContainer( dbUser, password );
152 | dbio.verifyMoreDataIntoContainer( dbUser, password, true );
153 | // stop database in preparation for restore
154 | dbio.runCypherQuery( dbUser, password, "STOP DATABASE neo4j", "system" );
155 |
156 | // do restore
157 | try(GenericContainer adminRestore = createAdminContainer( asDefaultUser ))
158 | {
159 | adminRestore.waitingFor( Wait.forLogMessage( ".*Restore of database .* completed successfully.*", 1 )
160 | .withStartupTimeout( Duration.ofSeconds( 180 ) ) )
161 | .withCommand( "neo4j-admin",
162 | "database",
163 | "restore",
164 | "--overwrite-destination=true",
165 | "--from-path=/backups/" + backupFile.getName(),
166 | "neo4j" );
167 | temporaryFolderManager.mountHostFolderAsVolume( adminRestore, backupDir, "/backups" );
168 | temporaryFolderManager.mountHostFolderAsVolume( adminRestore, dataDir, "/data" );
169 | adminRestore.start();
170 | dbio.runCypherQuery( dbUser, password, "START DATABASE neo4j", "system" );
171 |
172 | // verify new stuff is missing
173 | dbio.verifyMoreDataIntoContainer( dbUser, password, false );
174 | } //adminRestore out of scope here
175 | } // neo4j container goes out of scope here
176 | }
177 | }
178 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/src/test/java/com/neo4j/docker/utils/Neo4jVersionTest.java:
--------------------------------------------------------------------------------
```java
1 | package com.neo4j.docker.utils;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.Test;
5 |
6 | public class Neo4jVersionTest {
7 |
8 | @Test
9 | public void testIsOlderThan_majorDifferent()
10 | {
11 | Neo4jVersion newer = new Neo4jVersion( 3, 5, 1 );
12 | Neo4jVersion older = new Neo4jVersion( 2, 0, 0 );
13 | testOlderVsNewer( newer, older );
14 | }
15 |
16 | @Test
17 | public void testIsOlderThan_minorDifferent()
18 | {
19 | Neo4jVersion newer = new Neo4jVersion( 3, 5, 1 );
20 | Neo4jVersion older = new Neo4jVersion( 3, 0, 0 );
21 | testOlderVsNewer( newer, older );
22 | }
23 |
24 | @Test
25 | public void testIsOlderThan_patchDifferent()
26 | {
27 | Neo4jVersion newer = new Neo4jVersion( 3, 5, 1 );
28 | Neo4jVersion older = new Neo4jVersion( 3, 5, 0 );
29 | testOlderVsNewer( newer, older );
30 | }
31 |
32 | @Test
33 | public void testIsOlderThan_majorLessMinorMore()
34 | {
35 | Neo4jVersion newer = new Neo4jVersion( 3, 5, 1 );
36 | Neo4jVersion older = new Neo4jVersion( 2, 7, 0 );
37 | testOlderVsNewer( newer, older );
38 | }
39 |
40 | @Test
41 | public void testIsOlderThan_minorLessPatchMore()
42 | {
43 | Neo4jVersion newer = new Neo4jVersion( 3, 5, 0 );
44 | Neo4jVersion older = new Neo4jVersion( 3, 2, 12 );
45 | testOlderVsNewer( newer, older );
46 | }
47 |
48 | @Test
49 | public void testSamePatch_isEqualAndAtLeast()
50 | {
51 | Neo4jVersion newer = new Neo4jVersion( 3, 5, 0 );
52 | Neo4jVersion older = new Neo4jVersion( 3, 5, 0 );
53 |
54 | Assertions.assertFalse( newer.isOlderThan( older ) ,
55 | String.format( "Didn't detect that %s is newer than %s using isOlderThan", newer, older ) );
56 | Assertions.assertFalse( older.isNewerThan( newer ) ,
57 | String.format( "Didn't detect that %s is older than %s using isNewerThan", older, newer ) );
58 | Assertions.assertTrue( newer.isAtLeastVersion( older ),
59 | String.format( "Didn't detect that %s is at least %s using isAtLeastVersion", newer, older ) );
60 | Assertions.assertTrue( newer.isEqual(older),
61 | String.format( "Didn't detect that %s is equal to %s using isEqual", newer, older ) );
62 | }
63 |
64 | @Test
65 | public void testSamePatch_isEqualAndAtLeast_differentBuild()
66 | {
67 | Neo4jVersion newer = new Neo4jVersion( 3, 5, 0, "-11" );
68 | Neo4jVersion older = new Neo4jVersion( 3, 5, 0, "-10" );
69 |
70 | Assertions.assertFalse( newer.isOlderThan( older ) ,
71 | String.format( "Didn't detect that %s is newer than %s using isOlderThan", newer, older ) );
72 | Assertions.assertFalse( older.isNewerThan( newer ) ,
73 | String.format( "Didn't detect that %s is older than %s using isNewerThan", older, newer ) );
74 | Assertions.assertTrue( newer.isAtLeastVersion( older ),
75 | String.format( "Didn't detect that %s is at least %s using isAtLeastVersion", newer, older ) );
76 | Assertions.assertTrue( newer.isEqual(older),
77 | String.format( "Didn't detect that %s is equal to %s using isEqual", newer, older ) );
78 | }
79 |
80 | private void testOlderVsNewer( Neo4jVersion newer, Neo4jVersion older )
81 | {
82 | // isOlderThan
83 | Assertions.assertTrue( older.isOlderThan( newer ) ,
84 | String.format( "Didn't detect that %s is older than %s using isOlderThan", older, newer ) );
85 | Assertions.assertFalse( newer.isOlderThan( older ) ,
86 | String.format( "Didn't detect that %s is newer than %s using isOlderThan", newer, older ) );
87 |
88 | // isNewerThan
89 | Assertions.assertTrue( newer.isNewerThan( older ),
90 | String.format( "Didn't detect that %s is newer than %s using isNewerThan", newer, older ) );
91 | Assertions.assertFalse( older.isNewerThan( newer ) ,
92 | String.format( "Didn't detect that %s is older than %s using isNewerThan", older, newer ) );
93 |
94 | // isAtLeastVersion
95 | Assertions.assertTrue( newer.isAtLeastVersion( older ),
96 | String.format( "Didn't detect that %s is newer than %s using isAtLeastVersion", newer, older ) );
97 | Assertions.assertFalse( older.isAtLeastVersion( newer ) ,
98 | String.format( "Didn't detect that %s is older than %s using isAtLeastVersion", older, newer ) );
99 |
100 | Assertions.assertFalse( newer.isEqual(older),
101 | String.format( "%s incorrectly is equal to %s", newer, older ) );
102 | Assertions.assertNotEquals( newer, older, String.format( "%s incorrectly is equal to %s", newer, older ) );
103 | }
104 |
105 | @Test
106 | public void testIsOlderThan_sameReleaseReturnsFalse()
107 | {
108 | Neo4jVersion version = new Neo4jVersion( 3, 5, 1 );
109 |
110 | Assertions.assertFalse( version.isOlderThan( version ) ,
111 | "A release should not be older than itself" );
112 | Assertions.assertFalse( version.isNewerThan( version ) ,
113 | "A release should not be newer than itself" );
114 | }
115 |
116 | @Test
117 | public void testFromVersionString_releaseFormat()
118 | {
119 | Neo4jVersion version = Neo4jVersion.fromVersionString( "4.4.7" );
120 | Assertions.assertEquals( 4, version.major, "Did not parse major number from " + version );
121 | Assertions.assertEquals( 4, version.minor, "Did not parse minor number from " + version );
122 | Assertions.assertEquals( 7, version.patch, "Did not parse patch number from " + version );
123 | }
124 |
125 | @Test
126 | public void testFromVersionString_BSPFormat()
127 | {
128 | Neo4jVersion version = Neo4jVersion.fromVersionString( "4.4.7-12345" );
129 | Assertions.assertEquals( 4, version.major, "Did not parse major number from " + version );
130 | Assertions.assertEquals( 4, version.minor, "Did not parse minor number from " + version );
131 | Assertions.assertEquals( 7, version.patch, "Did not parse patch number from " + version );
132 | Assertions.assertEquals( "-12345", version.label, "Did not build number from " + version );
133 | }
134 |
135 | @Test
136 | public void testFromVersionString_calver_releaseFormat()
137 | {
138 | Neo4jVersion version = Neo4jVersion.fromVersionString( "2024.10.0" );
139 | Assertions.assertEquals( 2024, version.major, "Did not parse major number from " + version );
140 | Assertions.assertEquals( 10, version.minor, "Did not parse minor number from " + version );
141 | Assertions.assertEquals( 0, version.patch, "Did not parse patch number from " + version );
142 | }
143 |
144 | @Test
145 | public void testFromVersionString_calver_BSPFormat()
146 | {
147 | Neo4jVersion version = Neo4jVersion.fromVersionString( "2024.10.0-1234" );
148 | Assertions.assertEquals( 2024, version.major, "Did not parse major number from " + version );
149 | Assertions.assertEquals( 10, version.minor, "Did not parse minor number from " + version );
150 | Assertions.assertEquals( 0, version.patch, "Did not parse patch number from " + version );
151 | Assertions.assertEquals( "-1234", version.label, "Did not build number from " + version );
152 | }
153 |
154 | @Test
155 | public void testFromVersionString_calver_OneMonthDigit()
156 | {
157 | // an incorrect format, but would be useful if it was handled correctly
158 | Neo4jVersion version = Neo4jVersion.fromVersionString( "2027.1.5-99" );
159 | Assertions.assertEquals( 2027, version.major, "Did not parse major number from " + version );
160 | Assertions.assertEquals( 1, version.minor, "Did not parse minor number from " + version );
161 | Assertions.assertEquals( 5, version.patch, "Did not parse patch number from " + version );
162 | Assertions.assertEquals( "-99", version.label, "Did not build number from " + version );
163 | }
164 |
165 | @Test
166 | void testToStringCalVer()
167 | {
168 | Neo4jVersion version = Neo4jVersion.fromVersionString( "2025.2.0-1234" );
169 | String outStr = version.toString();
170 | Assertions.assertEquals( "2025.02.0-1234", outStr );
171 | }
172 | }
173 |
```
--------------------------------------------------------------------------------
/neo4j/docker-neo4j/docker-image-src/3.1/docker-entrypoint.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash -eu
2 |
3 | cmd="$1"
4 |
5 | function running_as_root
6 | {
7 | test "$(id -u)" = "0"
8 | }
9 |
10 | # If we're running as root, then run as the neo4j user. Otherwise
11 | # docker is running with --user and we simply use that user. Note
12 | # that su-exec, despite its name, does not replicate the functionality
13 | # of exec, so we need to use both
14 | if running_as_root; then
15 | userid="neo4j"
16 | groupid="neo4j"
17 | exec_cmd="exec su-exec neo4j"
18 | else
19 | userid="$(id -u)"
20 | groupid="$(id -g)"
21 | exec_cmd="exec"
22 | fi
23 | readonly userid
24 | readonly groupid
25 | readonly exec_cmd
26 |
27 | # Need to chown the home directory - but a user might have mounted a
28 | # volume here (notably a conf volume). So take care not to chown
29 | # volumes (stuff not owned by neo4j)
30 | if running_as_root; then
31 | # Non-recursive chown for the base directory
32 | chown "${userid}":"${groupid}" "${NEO4J_HOME}"
33 | chmod 700 "${NEO4J_HOME}"
34 | fi
35 |
36 | while IFS= read -r -d '' dir
37 | do
38 | if running_as_root && [[ "$(stat -c %U "${dir}")" = "neo4j" ]]; then
39 | # Using mindepth 1 to avoid the base directory here so recursive is OK
40 | chown -R "${userid}":"${groupid}" "${dir}"
41 | chmod -R 700 "${dir}"
42 | fi
43 | done < <(find "${NEO4J_HOME}" -type d -mindepth 1 -maxdepth 1 -print0)
44 |
45 | # Data dir is chowned later
46 |
47 | if [ "${cmd}" == "dump-config" ]; then
48 | if [ -d /conf ]; then
49 | ${exec_cmd} cp --recursive "${NEO4J_HOME}"/conf/* /conf
50 | exit 0
51 | else
52 | echo >&2 "You must provide a /conf volume"
53 | exit 1
54 | fi
55 | fi
56 |
57 | # Env variable naming convention:
58 | # - prefix NEO4J_
59 | # - double underscore char '__' instead of single underscore '_' char in the setting name
60 | # - underscore char '_' instead of dot '.' char in the setting name
61 | # Example:
62 | # NEO4J_dbms_tx__log_rotation_retention__policy env variable to set
63 | # dbms.tx_log.rotation.retention_policy setting
64 |
65 | # Backward compatibility - map old hardcoded env variables into new naming convention (if they aren't set already)
66 | # Set some to default values if unset
67 | : ${NEO4J_dbms_tx__log_rotation_retention__policy:=${NEO4J_dbms_txLog_rotation_retentionPolicy:-"100M size"}}
68 | : ${NEO4J_wrapper_java_additional:=${NEO4J_UDC_SOURCE:-"-Dneo4j.ext.udc.source=docker"}}
69 | : ${NEO4J_dbms_memory_heap_initial__size:=${NEO4J_dbms_memory_heap_maxSize:-"512M"}}
70 | : ${NEO4J_dbms_memory_heap_max__size:=${NEO4J_dbms_memory_heap_maxSize:-"512M"}}
71 | : ${NEO4J_dbms_unmanaged__extension__classes:=${NEO4J_dbms_unmanagedExtensionClasses:-}}
72 | : ${NEO4J_dbms_allow__format__migration:=${NEO4J_dbms_allowFormatMigration:-}}
73 | : ${NEO4J_dbms_connectors_default__advertised__address:=${NEO4J_dbms_connectors_defaultAdvertisedAddress:-}}
74 | : ${NEO4J_ha_server__id:=${NEO4J_ha_serverId:-}}
75 | : ${NEO4J_ha_initial__hosts:=${NEO4J_ha_initialHosts:-}}
76 | : ${NEO4J_causal__clustering_expected__core__cluster__size:=${NEO4J_causalClustering_expectedCoreClusterSize:-}}
77 | : ${NEO4J_causal__clustering_initial__discovery__members:=${NEO4J_causalClustering_initialDiscoveryMembers:-}}
78 | : ${NEO4J_causal__clustering_discovery__listen__address:=${NEO4J_causalClustering_discoveryListenAddress:-"0.0.0.0:5000"}}
79 | : ${NEO4J_causal__clustering_discovery__advertised__address:=${NEO4J_causalClustering_discoveryAdvertisedAddress:-"$(hostname):5000"}}
80 | : ${NEO4J_causal__clustering_transaction__listen__address:=${NEO4J_causalClustering_transactionListenAddress:-"0.0.0.0:6000"}}
81 | : ${NEO4J_causal__clustering_transaction__advertised__address:=${NEO4J_causalClustering_transactionAdvertisedAddress:-"$(hostname):6000"}}
82 | : ${NEO4J_causal__clustering_raft__listen__address:=${NEO4J_causalClustering_raftListenAddress:-"0.0.0.0:7000"}}
83 | : ${NEO4J_causal__clustering_raft__advertised__address:=${NEO4J_causalClustering_raftAdvertisedAddress:-"$(hostname):7000"}}
84 |
85 | : ${NEO4J_dbms_connectors_default__listen__address:="0.0.0.0"}
86 | : ${NEO4J_dbms_connector_http_listen__address:="0.0.0.0:7474"}
87 | : ${NEO4J_dbms_connector_https_listen__address:="0.0.0.0:7473"}
88 | : ${NEO4J_dbms_connector_bolt_listen__address:="0.0.0.0:7687"}
89 | : ${NEO4J_ha_host_coordination:="$(hostname):5001"}
90 | : ${NEO4J_ha_host_data:="$(hostname):6001"}
91 |
92 | # unset old hardcoded unsupported env variables
93 | unset NEO4J_dbms_txLog_rotation_retentionPolicy NEO4J_UDC_SOURCE \
94 | NEO4J_dbms_memory_heap_maxSize NEO4J_dbms_memory_heap_maxSize \
95 | NEO4J_dbms_unmanagedExtensionClasses NEO4J_dbms_allowFormatMigration \
96 | NEO4J_dbms_connectors_defaultAdvertisedAddress NEO4J_ha_serverId \
97 | NEO4J_ha_initialHosts NEO4J_causalClustering_expectedCoreClusterSize \
98 | NEO4J_causalClustering_initialDiscoveryMembers \
99 | NEO4J_causalClustering_discoveryListenAddress \
100 | NEO4J_causalClustering_discoveryAdvertisedAddress \
101 | NEO4J_causalClustering_transactionListenAddress \
102 | NEO4J_causalClustering_transactionAdvertisedAddress \
103 | NEO4J_causalClustering_raftListenAddress \
104 | NEO4J_causalClustering_raftAdvertisedAddress
105 |
106 | if [ -d /conf ]; then
107 | find /conf -type f -exec cp {} "${NEO4J_HOME}"/conf \;
108 | fi
109 |
110 | if [ -d /ssl ]; then
111 | NEO4J_dbms_directories_certificates="/ssl"
112 | fi
113 |
114 | if [ -d /plugins ]; then
115 | NEO4J_dbms_directories_plugins="/plugins"
116 | fi
117 |
118 | if [ -d /logs ]; then
119 | NEO4J_dbms_directories_logs="/logs"
120 | fi
121 |
122 | if [ -d /import ]; then
123 | NEO4J_dbms_directories_import="/import"
124 | fi
125 |
126 | if [ -d /metrics ]; then
127 | NEO4J_dbms_directories_metrics="/metrics"
128 | fi
129 |
130 | # set the neo4j initial password only if you run the database server
131 | if [ "${cmd}" == "neo4j" ]; then
132 | if [ "${NEO4J_AUTH:-}" == "none" ]; then
133 | NEO4J_dbms_security_auth__enabled=false
134 | elif [[ "${NEO4J_AUTH:-}" == neo4j/* ]]; then
135 | password="${NEO4J_AUTH#neo4j/}"
136 | if [ "${password}" == "neo4j" ]; then
137 | echo >&2 "Invalid value for password. It cannot be 'neo4j', which is the default."
138 | exit 1
139 | fi
140 | # Will exit with error if users already exist (and print a message explaining that)
141 | bin/neo4j-admin set-initial-password "${password}" || true
142 | elif [ -n "${NEO4J_AUTH:-}" ]; then
143 | echo >&2 "Invalid value for NEO4J_AUTH: '${NEO4J_AUTH}'"
144 | exit 1
145 | fi
146 | fi
147 |
148 | # list env variables with prefix NEO4J_ and create settings from them
149 | unset NEO4J_AUTH NEO4J_SHA256 NEO4J_TARBALL
150 | for i in $( set | grep ^NEO4J_ | awk -F'=' '{print $1}' | sort -rn ); do
151 | setting=$(echo ${i} | sed 's|^NEO4J_||' | sed 's|_|.|g' | sed 's|\.\.|_|g')
152 | value=$(echo ${!i})
153 | # Don't allow settings with no value or settings that start with a number (neo4j converts settings to env variables and you cannot have an env variable that starts with a number)
154 | if [[ -n ${value} ]]; then
155 | if [[ ! "${setting}" =~ ^[0-9]+.*$ ]]; then
156 | if grep -q -F "${setting}=" "${NEO4J_HOME}"/conf/neo4j.conf; then
157 | # Remove any lines containing the setting already
158 | sed --in-place "/^${setting}=.*/d" "${NEO4J_HOME}"/conf/neo4j.conf
159 | fi
160 | # Then always append setting to file
161 | echo "${setting}=${value}" >> "${NEO4J_HOME}"/conf/neo4j.conf
162 | else
163 | echo >&2 "WARNING: ${setting} not written to conf file because settings that start with a number are not permitted"
164 | fi
165 | fi
166 | done
167 |
168 | # Chown the data dir now that (maybe) an initial password has been
169 | # set (this is a file in the data dir)
170 | if running_as_root; then
171 | chmod -R 777 /data
172 | chown -R "${userid}":"${groupid}" /data
173 | fi
174 |
175 | # if we're running as root and the logs directory is not writable by the neo4j user, then chown it.
176 | # this situation happens if no user is passed to docker run and the /logs directory is mounted.
177 | if running_as_root && [[ "$(stat -c %U /logs)" != "neo4j" ]]; then
178 | #if [[ $(stat -c %u /logs) != $(id -u "${userid}") ]]; then
179 | echo "/logs directory is not writable. Changing the directory owner to ${userid}:${groupid}"
180 | # chown the log dir if it's not writable
181 | chmod -R 777 /logs
182 | chown -R "${userid}":"${groupid}" /logs
183 | fi
184 |
185 | # If we're running as a non-default user and we can't write to the logs directory then user needs to change directory permissions manually.
186 | # This happens if a user is passed to docker run and an unwritable log directory is mounted.
187 | if ! running_as_root && [[ ! -w /logs ]]; then
188 | echo "User does not have write permissions to mounted log directory."
189 | echo "Manually grant write permissions for the directory and try again."
190 | exit 1
191 | fi
192 |
193 | [ -f "${EXTENSION_SCRIPT:-}" ] && . ${EXTENSION_SCRIPT}
194 |
195 | # Use su-exec to drop privileges to neo4j user
196 | # Note that su-exec, despite its name, does not replicate the
197 | # functionality of exec, so we need to use both
198 | if [ "${cmd}" == "neo4j" ]; then
199 | ${exec_cmd} neo4j console
200 | else
201 | ${exec_cmd} "$@"
202 | fi
203 |
```