├── .editorconfig ├── .github └── workflows │ ├── check_label.yml │ └── test.yaml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CONTRIBUTORS.md ├── LICENSE ├── Makefile ├── README.md ├── certs ├── gen_cert.sh ├── openssl.cnf ├── test.2.crt ├── test.2.csr ├── test.2.key ├── test.2.password ├── test.3.CA.crt ├── test.3.CA.csr ├── test.3.CA.key ├── test.3.CA.srl ├── test.3.derive.crt ├── test.3.derive.csr ├── test.3.derive.key ├── test.3.ext ├── test.ca.key ├── test.ca.password ├── test.ca.pem ├── test.ca.srl ├── test.derive.crt ├── test.derive.csr ├── test.derive.key ├── test.nopassword.crt ├── test.nopassword.csr ├── test.nopassword.derive.crt ├── test.nopassword.key ├── test.nopassword.srl └── test.password ├── client_test.go ├── configs.go ├── connection.go ├── connection_pool.go ├── const.go ├── examples ├── basic_example │ └── graph_client_basic_example.go ├── goroutines_example │ └── graph_client_goroutines_example.go ├── json_example │ └── parse_json_example.go ├── parameter_example │ └── parameter_example.go ├── session_pool_example │ └── session_pool_example.go └── ssl_example │ └── graph_client_ssl_example.go ├── go.mod ├── go.sum ├── host_address.go ├── label.go ├── label_test.go ├── logger.go ├── nebula-docker-compose ├── .env ├── docker-compose-ssl.yaml ├── docker-compose.yaml └── secrets │ ├── client.crt │ ├── client.key │ ├── readme.md │ ├── root.crt │ ├── root.key │ ├── run.sh │ ├── server.crt │ └── server.key ├── nebula ├── constants.go ├── graph │ ├── constants.go │ ├── graphservice.go │ └── ttypes.go ├── meta │ ├── constants.go │ ├── metaservice.go │ └── ttypes.go ├── storage │ ├── constants.go │ ├── graphstorageservice.go │ ├── storageadminservice.go │ └── ttypes.go └── ttypes.go ├── result_set.go ├── result_set_test.go ├── schema_manager.go ├── schema_manager_test.go ├── session.go ├── session_pool.go ├── session_pool_test.go ├── session_test.go ├── ssl_connection_test.go ├── ssl_sessionpool_test.go ├── util.go ├── util_test.go └── value_wrapper.go /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | insert_final_newline = true 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | indent_style = space 8 | indent_size = 2 9 | 10 | [{Makefile,go.mod,go.sum,*.go}] 11 | indent_style = tab 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.github/workflows/check_label.yml: -------------------------------------------------------------------------------- 1 | name: Auto label 2 | 3 | on: 4 | issues: 5 | types: 6 | - reopened 7 | - opened 8 | - labeled 9 | - unlabeled 10 | - closed 11 | 12 | env: 13 | GH_PAT: ${{ secrets.GITHUB_TOKEN }} 14 | EVENT: ${{ toJSON(github.event)}} 15 | EVENT_NAME: ${{ github.event_name}} 16 | 17 | jobs: 18 | sync: 19 | name: auto label 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: HarrisChu/auto_label@v1 23 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: 3 | push: 4 | branches: [ master, 'release-**'] 5 | pull_request: 6 | branches: [ master, 'release-**'] 7 | schedule: 8 | - cron: "0 6 * * *" 9 | 10 | jobs: 11 | lint: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Lint 16 | run: | 17 | make lint 18 | 19 | go-client: 20 | runs-on: ubuntu-latest 21 | strategy: 22 | matrix: 23 | goVer: ['1.18', '1.19', '1.20', '1.21', '1.22', '1.23'] 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: Setup go ${{ matrix.goVer }} 27 | uses: actions/setup-go@v1 28 | with: 29 | go-version: ${{ matrix.goVer }} 30 | - name: Test with GO test 31 | run: | 32 | make up 33 | sleep 10 34 | make test 35 | - name: Upload coverage to Codecov 36 | uses: codecov/codecov-action@v3 37 | - name: down 38 | if: always() 39 | run: | 40 | make down 41 | go-client-ssl: 42 | runs-on: ubuntu-latest 43 | strategy: 44 | matrix: 45 | goVer: ['1.18', '1.19', '1.20', '1.21', '1.22', '1.23'] 46 | steps: 47 | - uses: actions/checkout@v2 48 | - name: Setup go ${{ matrix.goVer }} 49 | uses: actions/setup-go@v1 50 | with: 51 | go-version: ${{ matrix.goVer }} 52 | - name: Test SSL connection with CA 53 | run: | 54 | make up-ssl 55 | sleep 10 56 | make ssl-test 57 | - name: Test SSL connection self-signed 58 | run: | 59 | make up-ssl 60 | sleep 10 61 | make ssl-test-self-signed 62 | - name: down 63 | if: always() 64 | run: | 65 | make down 66 | go-client-example: 67 | runs-on: ubuntu-latest 68 | strategy: 69 | matrix: 70 | goVer: ['1.18', '1.19', '1.20', '1.21', '1.22', '1.23'] 71 | steps: 72 | - uses: actions/checkout@v2 73 | - name: Setup go ${{ matrix.goVer }} 74 | uses: actions/setup-go@v1 75 | with: 76 | go-version: ${{ matrix.goVer }} 77 | - name: Run examples 78 | run: | 79 | make up 80 | sleep 10 81 | make run-examples 82 | - name: down 83 | if: always() 84 | run: | 85 | make down 86 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | .vscode/ 8 | 9 | # Test binary, build with `go test -c` 10 | *.test 11 | 12 | # Docker compose for testing 13 | nebula-docker-compose/data/ 14 | nebula-docker-compose/logs/ 15 | # Output of the go coverage tool, specifically when used with LiteIDE 16 | *.out 17 | 18 | .DS_Store 19 | 20 | .idea/ 21 | 22 | # ssl 23 | *.cnf 24 | *.csr 25 | *.srl 26 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, caste, color, religion, or sexual 11 | identity and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the overall 27 | community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or advances of 32 | any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email address, 36 | without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | [INSERT CONTACT METHOD]. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series of 87 | actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or permanent 94 | ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within the 114 | community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.1, available at 120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 121 | 122 | Community Impact Guidelines were inspired by 123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 127 | [https://www.contributor-covenant.org/translations][translations]. 128 | 129 | [homepage]: https://www.contributor-covenant.org 130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 131 | [Mozilla CoC]: https://github.com/mozilla/diversity 132 | [FAQ]: https://www.contributor-covenant.org/faq 133 | [translations]: https://www.contributor-covenant.org/translations 134 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | See [How to contribute](https://github.com/vesoft-inc/nebula-community/blob/master/Contributors/how-to-contribute.md) for details about how to contribute to **Nebula Graph**. 2 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | Nebula-go contributors 2 | ====================== 3 | 4 | **[Full contributors list](https://github.com/vesoft-inc/nebula-go/graphs/contributors).** 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build unit test fmt up up-ssl down ssl-test run-examples 2 | 3 | default: build 4 | 5 | build: fmt 6 | go mod tidy 7 | go build 8 | unit: 9 | go mod tidy 10 | go test -v -race --covermode=atomic --coverprofile coverage.out 11 | test: 12 | go mod tidy 13 | go test -v -race --tags=integration --covermode=atomic --coverprofile coverage.out 14 | 15 | fmt: 16 | go fmt 17 | 18 | lint: 19 | @test -z `gofmt -l *.go` || (echo "Please run 'make fmt' to format Go code" && exit 1) 20 | 21 | up: 22 | cd ./nebula-docker-compose && docker compose up -d 23 | 24 | up-ssl: 25 | cd ./nebula-docker-compose && enable_ssl=true docker compose -f docker-compose-ssl.yaml up -d 26 | 27 | down: 28 | cd ./nebula-docker-compose && docker compose down -v 29 | 30 | ssl-test: 31 | ssl_test=true go test -v --tags=integration -run TestSslConnection; 32 | ssl_test=true go test -v --tags=integration -run TestSslSessionPool; 33 | 34 | ssl-test-self-signed: 35 | self_signed=true go test -v --tags=integration -run TestSslConnection; 36 | 37 | run-examples: 38 | go run examples/basic_example/graph_client_basic_example.go && \ 39 | go run examples/parameter_example/parameter_example.go && \ 40 | go run examples/goroutines_example/graph_client_goroutines_example.go && \ 41 | go run examples/json_example/parse_json_example.go && \ 42 | go run examples/session_pool_example/session_pool_example.go 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nebula-go 2 | 3 | [![Go Reference](https://pkg.go.dev/badge/github.com/vesoft-inc/nebula-go/v3.svg)](https://pkg.go.dev/github.com/vesoft-inc/nebula-go/v3) 4 | ![functional tests](https://github.com/vesoft-inc/nebula-go/actions/workflows/test.yaml/badge.svg) 5 | [![codecov](https://codecov.io/gh/vesoft-inc/nebula-go/branch/master/graph/badge.svg?token=dzUo5KdSux)](https://codecov.io/gh/vesoft-inc/nebula-go) 6 | 7 | **IMPORTANT: Code of Nebula go client has been transferred from [nebula-clients](https://github.com/vesoft-inc/nebula-clients) to this repository(nebula-go), and new releases in the future will be published in this repository. 8 | Please update your `go.mod` and imports correspondingly.** 9 | 10 | Official Nebula Go client which communicates with the server using [fbthrift](https://github.com/vesoft-inc/fbthrift/). Currently the latest stable release is **[v3.4.0](https://github.com/vesoft-inc/nebula-go/tree/release-v3.4)** 11 | 12 | The code in **master branch** will be updated to accommodate the nightly changes made in NebulaGraph. 13 | To Use the console with a stable release of NebulaGraph, please check the branches and use the corresponding version. 14 | 15 | | Client version | Nebula Service Version | 16 | | :---------------------------------------------------------------------: | :--------------------: | 17 | | **[v1.0.0](https://github.com/vesoft-inc/nebula-go/tree/v1.0)** | 1.x.x | 18 | | **[v2.0.0-ga](https://github.com/vesoft-inc/nebula-go/tree/v2.0.0-ga)** | 2.0.0-ga, 2.0.1 | 19 | | **[v2.5.1](https://github.com/vesoft-inc/nebula-go/tree/v2.5.1)** | 2.5.0 | 20 | | **[v2.6.0](https://github.com/vesoft-inc/nebula-go/tree/v2.6.0)** | 2.6.0 | 21 | | **[v3.0.0](https://github.com/vesoft-inc/nebula-go/tree/v3.0.0)** | 3.0.0 | 22 | | **[v3.1.x](https://github.com/vesoft-inc/nebula-go/tree/v3.1.0)** | 3.1.x | 23 | | **[v3.2.x](https://github.com/vesoft-inc/nebula-go/tree/v3.2.0)** | 3.1.x-3.2.x | 24 | | **[v3.3.x](https://github.com/vesoft-inc/nebula-go/tree/v3.3.0)** | 3.1.x-3.3.x | 25 | | **[v3.4.x](https://github.com/vesoft-inc/nebula-go/tree/v3.4.0)** | 3.1.x-3.4.x | 26 | | **[v3.5.x](https://github.com/vesoft-inc/nebula-go/tree/v3.5.0)** | 3.1.x-3.5.x | 27 | | **[master](https://github.com/vesoft-inc/nebula-go/tree/master)** | 3.x-nightly | 28 | 29 | Please be careful not to modify the files in the nebula directory, these codes were all generated by fbthrift. 30 | 31 | **NOTE** Installing Nebula Go v2.5.0 could cause **checksum mismatch**, use v2.5.1 instead. 32 | 33 | ## Install & Update 34 | 35 | ```shell 36 | $ go get -u -v github.com/vesoft-inc/nebula-go/v3@master 37 | ``` 38 | 39 | You can specify the version of Nebula-go by substituting `` in `$ go get -u -v github.com/vesoft-inc/nebula-go@`. 40 | For example: 41 | 42 | for v3: `$ go get -u -v github.com/vesoft-inc/nebula-go/v3@v3.4.0` 43 | 44 | for v2: `$ go get -u -v github.com/vesoft-inc/nebula-go/v2@v2.6.0` 45 | 46 | **Note**: You will get a message like this if you don't specify a tag: 47 | 48 | > ```shell 49 | > $ go get -u -v github.com/vesoft-inc/nebula-go/v2@master 50 | > go: github.com/vesoft-inc/nebula-go/v2 master => v2.0.0-20210506025434-97d4168c5c4d 51 | > ``` 52 | > 53 | > Here the `20210506025434-97d4168c5c4d` is a version tag auto-generated by GitHub using commit date and SHA. 54 | > This should match the latest commit in the master branch. 55 | 56 | ## Usage example 57 | 58 | ### Mininal example 59 | 60 | Suppose you already initialized your space and defined the schema as: 61 | 1. Vertex `person` with properties `name: string`, `age: int` 62 | 2. Edge `like` with properties `likeness: double` 63 | 64 | You can query the Nebula by the minimal example: 65 | 66 | ```go 67 | package main 68 | 69 | import ( 70 | nebula "github.com/vesoft-inc/nebula-go/v3" 71 | ) 72 | 73 | type Person struct { 74 | Name string `nebula:"name"` 75 | Age int `nebula:"age"` 76 | Likeness float64 `nebula:"likeness"` 77 | } 78 | 79 | func main() { 80 | hostAddress := nebula.HostAddress{Host: "127.0.0.1", Port: 3699} 81 | 82 | config, err := nebula.NewSessionPoolConf( 83 | "user", 84 | "password", 85 | []nebula.HostAddress{hostAddress}, 86 | "space_name", 87 | ) 88 | 89 | sessionPool, err := nebula.NewSessionPool(*config, nebula.DefaultLogger{}) 90 | 91 | query := `GO FROM 'Bob' OVER like YIELD 92 | $^.person.name AS name, 93 | $^.person.age AS age, 94 | like.likeness AS likeness` 95 | 96 | resultSet, err := sessionPool.Execute(query) 97 | if err != nil { 98 | panic(err) 99 | } 100 | 101 | var personList []Person 102 | resultSet.Scan(&personList) 103 | } 104 | ``` 105 | 106 | ### More examples 107 | 108 | [Simple Code Example](https://github.com/vesoft-inc/nebula-go/tree/master/examples/basic_example/graph_client_basic_example.go) 109 | 110 | [Code Example with Goroutines](https://github.com/vesoft-inc/nebula-go/tree/master/examples/goroutines_example/graph_client_goroutines_example.go) 111 | 112 | [Session Pool Example](https://github.com/vesoft-inc/nebula-go/blob/master/examples/session_pool_example/session_pool_example.go) 113 | 114 | There are some limitations while using the session pool: 115 | 1. There MUST be an existing space in the DB before initializing the session pool. 116 | 2. Each session pool is corresponding to a single USER and a single Space. This is to ensure that the user's access control is consistent. i.g. The same user may have different access privileges in different spaces. If you need to run queries in different spaces, you may have multiple session pools. 117 | 3. Every time when `sessionPool.Execute()` is called, the session will execute the query in the space set in the session pool config. 118 | 4. Commands that alter passwords or drop users should NOT be executed via session pool. 119 | 120 | ## Code of Conduct 121 | 122 | This project and everyone participating in it is governed by the 123 | [Vesoft Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are 124 | expected to uphold this code. 125 | 126 | ## Licensing 127 | 128 | **Nebula GO** is under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0) license, so you can freely download, modify, and deploy the source code to meet your needs. You can also freely deploy **Nebula GO** as a back-end service to support your SaaS deployment. 129 | -------------------------------------------------------------------------------- /certs/gen_cert.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | openssl genrsa -out test.3.CA.key 4096 4 | openssl req -new -out test.3.CA.csr -key test.3.CA.key 5 | openssl x509 -req -in test.3.CA.csr -out test.3.CA.crt -extfile openssl.cnf -extensions v3_ca -signkey test.3.CA.key -CAcreateserial -days 3650 6 | openssl genrsa -out test.3.derive.key 4096 7 | openssl req -new -out test.3.derive.csr -key test.3.derive.key 8 | openssl x509 -req -in test.3.derive.csr -out test.3.derive.crt -CA test.3.CA.crt -CAkey test.3.CA.key -CAcreateserial -days 3650 -extfile test.3.ext 9 | -------------------------------------------------------------------------------- /certs/openssl.cnf: -------------------------------------------------------------------------------- 1 | # 2 | # OpenSSL example configuration file. 3 | # See doc/man5/config.pod for more info. 4 | # 5 | # This is mostly being used for generation of certificate requests, 6 | # but may be used for auto loading of providers 7 | 8 | # Note that you can include other files from the main configuration 9 | # file using the .include directive. 10 | #.include filename 11 | 12 | # This definition stops the following lines choking if HOME isn't 13 | # defined. 14 | HOME = . 15 | 16 | # Use this in order to automatically load providers. 17 | openssl_conf = openssl_init 18 | 19 | # Comment out the next line to ignore configuration errors 20 | config_diagnostics = 1 21 | 22 | # Extra OBJECT IDENTIFIER info: 23 | # oid_file = $ENV::HOME/.oid 24 | oid_section = new_oids 25 | 26 | # To use this configuration file with the "-extfile" option of the 27 | # "openssl x509" utility, name here the section containing the 28 | # X.509v3 extensions to use: 29 | # extensions = 30 | # (Alternatively, use a configuration file that has only 31 | # X.509v3 extensions in its main [= default] section.) 32 | 33 | [ new_oids ] 34 | # We can add new OIDs in here for use by 'ca', 'req' and 'ts'. 35 | # Add a simple OID like this: 36 | # testoid1=1.2.3.4 37 | # Or use config file substitution like this: 38 | # testoid2=${testoid1}.5.6 39 | 40 | # Policies used by the TSA examples. 41 | tsa_policy1 = 1.2.3.4.1 42 | tsa_policy2 = 1.2.3.4.5.6 43 | tsa_policy3 = 1.2.3.4.5.7 44 | 45 | # For FIPS 46 | # Optionally include a file that is generated by the OpenSSL fipsinstall 47 | # application. This file contains configuration data required by the OpenSSL 48 | # fips provider. It contains a named section e.g. [fips_sect] which is 49 | # referenced from the [provider_sect] below. 50 | # Refer to the OpenSSL security policy for more information. 51 | # .include fipsmodule.cnf 52 | 53 | [openssl_init] 54 | providers = provider_sect 55 | 56 | # List of providers to load 57 | [provider_sect] 58 | default = default_sect 59 | # The fips section name should match the section name inside the 60 | # included fipsmodule.cnf. 61 | # fips = fips_sect 62 | 63 | # If no providers are activated explicitly, the default one is activated implicitly. 64 | # See man 7 OSSL_PROVIDER-default for more details. 65 | # 66 | # If you add a section explicitly activating any other provider(s), you most 67 | # probably need to explicitly activate the default provider, otherwise it 68 | # becomes unavailable in openssl. As a consequence applications depending on 69 | # OpenSSL may not work correctly which could lead to significant system 70 | # problems including inability to remotely access the system. 71 | [default_sect] 72 | # activate = 1 73 | 74 | 75 | #################################################################### 76 | [ ca ] 77 | default_ca = CA_default # The default ca section 78 | 79 | #################################################################### 80 | [ CA_default ] 81 | 82 | dir = ./demoCA # Where everything is kept 83 | certs = $dir/certs # Where the issued certs are kept 84 | crl_dir = $dir/crl # Where the issued crl are kept 85 | database = $dir/index.txt # database index file. 86 | #unique_subject = no # Set to 'no' to allow creation of 87 | # several certs with same subject. 88 | new_certs_dir = $dir/newcerts # default place for new certs. 89 | 90 | certificate = $dir/cacert.pem # The CA certificate 91 | serial = $dir/serial # The current serial number 92 | crlnumber = $dir/crlnumber # the current crl number 93 | # must be commented out to leave a V1 CRL 94 | crl = $dir/crl.pem # The current CRL 95 | private_key = $dir/private/cakey.pem # The private key 96 | 97 | x509_extensions = usr_cert # The extensions to add to the cert 98 | 99 | # Comment out the following two lines for the "traditional" 100 | # (and highly broken) format. 101 | name_opt = ca_default # Subject Name options 102 | cert_opt = ca_default # Certificate field options 103 | 104 | # Extension copying option: use with caution. 105 | # copy_extensions = copy 106 | 107 | # Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs 108 | # so this is commented out by default to leave a V1 CRL. 109 | # crlnumber must also be commented out to leave a V1 CRL. 110 | # crl_extensions = crl_ext 111 | 112 | default_days = 365 # how long to certify for 113 | default_crl_days= 30 # how long before next CRL 114 | default_md = default # use public key default MD 115 | preserve = no # keep passed DN ordering 116 | 117 | # A few difference way of specifying how similar the request should look 118 | # For type CA, the listed attributes must be the same, and the optional 119 | # and supplied fields are just that :-) 120 | policy = policy_match 121 | 122 | # For the CA policy 123 | [ policy_match ] 124 | countryName = match 125 | stateOrProvinceName = match 126 | organizationName = match 127 | organizationalUnitName = optional 128 | commonName = supplied 129 | emailAddress = optional 130 | 131 | # For the 'anything' policy 132 | # At this point in time, you must list all acceptable 'object' 133 | # types. 134 | [ policy_anything ] 135 | countryName = optional 136 | stateOrProvinceName = optional 137 | localityName = optional 138 | organizationName = optional 139 | organizationalUnitName = optional 140 | commonName = supplied 141 | emailAddress = optional 142 | 143 | #################################################################### 144 | [ req ] 145 | default_bits = 2048 146 | default_keyfile = privkey.pem 147 | distinguished_name = req_distinguished_name 148 | attributes = req_attributes 149 | x509_extensions = v3_ca # The extensions to add to the self signed cert 150 | 151 | # Passwords for private keys if not present they will be prompted for 152 | # input_password = secret 153 | # output_password = secret 154 | 155 | # This sets a mask for permitted string types. There are several options. 156 | # default: PrintableString, T61String, BMPString. 157 | # pkix : PrintableString, BMPString (PKIX recommendation before 2004) 158 | # utf8only: only UTF8Strings (PKIX recommendation after 2004). 159 | # nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). 160 | # MASK:XXXX a literal mask value. 161 | # WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. 162 | string_mask = utf8only 163 | 164 | # req_extensions = v3_req # The extensions to add to a certificate request 165 | 166 | [ req_distinguished_name ] 167 | countryName = Country Name (2 letter code) 168 | countryName_default = AU 169 | countryName_min = 2 170 | countryName_max = 2 171 | 172 | stateOrProvinceName = State or Province Name (full name) 173 | stateOrProvinceName_default = Some-State 174 | 175 | localityName = Locality Name (eg, city) 176 | 177 | 0.organizationName = Organization Name (eg, company) 178 | 0.organizationName_default = Internet Widgits Pty Ltd 179 | 180 | # we can do this but it is not needed normally :-) 181 | #1.organizationName = Second Organization Name (eg, company) 182 | #1.organizationName_default = World Wide Web Pty Ltd 183 | 184 | organizationalUnitName = Organizational Unit Name (eg, section) 185 | #organizationalUnitName_default = 186 | 187 | commonName = Common Name (e.g. server FQDN or YOUR name) 188 | commonName_max = 64 189 | 190 | emailAddress = Email Address 191 | emailAddress_max = 64 192 | 193 | # SET-ex3 = SET extension number 3 194 | 195 | [ req_attributes ] 196 | challengePassword = A challenge password 197 | challengePassword_min = 4 198 | challengePassword_max = 20 199 | 200 | unstructuredName = An optional company name 201 | 202 | [ usr_cert ] 203 | 204 | # These extensions are added when 'ca' signs a request. 205 | 206 | # This goes against PKIX guidelines but some CAs do it and some software 207 | # requires this to avoid interpreting an end user certificate as a CA. 208 | 209 | basicConstraints=CA:FALSE 210 | 211 | # This is typical in keyUsage for a client certificate. 212 | # keyUsage = nonRepudiation, digitalSignature, keyEncipherment 213 | 214 | # PKIX recommendations harmless if included in all certificates. 215 | subjectKeyIdentifier=hash 216 | authorityKeyIdentifier=keyid,issuer 217 | 218 | # This stuff is for subjectAltName and issuerAltname. 219 | # Import the email address. 220 | # subjectAltName=email:copy 221 | # An alternative to produce certificates that aren't 222 | # deprecated according to PKIX. 223 | # subjectAltName=email:move 224 | 225 | # Copy subject details 226 | # issuerAltName=issuer:copy 227 | 228 | # This is required for TSA certificates. 229 | # extendedKeyUsage = critical,timeStamping 230 | 231 | [ v3_req ] 232 | 233 | # Extensions to add to a certificate request 234 | 235 | basicConstraints = CA:FALSE 236 | keyUsage = nonRepudiation, digitalSignature, keyEncipherment 237 | 238 | [ v3_ca ] 239 | 240 | 241 | # Extensions for a typical CA 242 | 243 | 244 | # PKIX recommendation. 245 | 246 | subjectKeyIdentifier=hash 247 | 248 | authorityKeyIdentifier=keyid:always,issuer 249 | 250 | basicConstraints = critical,CA:true 251 | 252 | # Key usage: this is typical for a CA certificate. However since it will 253 | # prevent it being used as an test self-signed certificate it is best 254 | # left out by default. 255 | # keyUsage = cRLSign, keyCertSign 256 | 257 | # Include email address in subject alt name: another PKIX recommendation 258 | # subjectAltName=email:copy 259 | # Copy issuer details 260 | # issuerAltName=issuer:copy 261 | 262 | # DER hex encoding of an extension: beware experts only! 263 | # obj=DER:02:03 264 | # Where 'obj' is a standard or added object 265 | # You can even override a supported extension: 266 | # basicConstraints= critical, DER:30:03:01:01:FF 267 | 268 | [ crl_ext ] 269 | 270 | # CRL extensions. 271 | # Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. 272 | 273 | # issuerAltName=issuer:copy 274 | authorityKeyIdentifier=keyid:always 275 | 276 | [ proxy_cert_ext ] 277 | # These extensions should be added when creating a proxy certificate 278 | 279 | # This goes against PKIX guidelines but some CAs do it and some software 280 | # requires this to avoid interpreting an end user certificate as a CA. 281 | 282 | basicConstraints=CA:FALSE 283 | 284 | # This is typical in keyUsage for a client certificate. 285 | # keyUsage = nonRepudiation, digitalSignature, keyEncipherment 286 | 287 | # PKIX recommendations harmless if included in all certificates. 288 | subjectKeyIdentifier=hash 289 | authorityKeyIdentifier=keyid,issuer 290 | 291 | # This stuff is for subjectAltName and issuerAltname. 292 | # Import the email address. 293 | # subjectAltName=email:copy 294 | # An alternative to produce certificates that aren't 295 | # deprecated according to PKIX. 296 | # subjectAltName=email:move 297 | 298 | # Copy subject details 299 | # issuerAltName=issuer:copy 300 | 301 | # This really needs to be in place for it to be a proxy certificate. 302 | proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo 303 | 304 | #################################################################### 305 | [ tsa ] 306 | 307 | default_tsa = tsa_config1 # the default TSA section 308 | 309 | [ tsa_config1 ] 310 | 311 | # These are used by the TSA reply generation only. 312 | dir = ./demoCA # TSA root directory 313 | serial = $dir/tsaserial # The current serial number (mandatory) 314 | crypto_device = builtin # OpenSSL engine to use for signing 315 | signer_cert = $dir/tsacert.pem # The TSA signing certificate 316 | # (optional) 317 | certs = $dir/cacert.pem # Certificate chain to include in reply 318 | # (optional) 319 | signer_key = $dir/private/tsakey.pem # The TSA private key (optional) 320 | signer_digest = sha256 # Signing digest to use. (Optional) 321 | default_policy = tsa_policy1 # Policy if request did not specify it 322 | # (optional) 323 | other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) 324 | digests = sha1, sha256, sha384, sha512 # Acceptable message digests (mandatory) 325 | accuracy = secs:1, millisecs:500, microsecs:100 # (optional) 326 | clock_precision_digits = 0 # number of digits after dot. (optional) 327 | ordering = yes # Is ordering defined for timestamps? 328 | # (optional, default: no) 329 | tsa_name = yes # Must the TSA name be included in the reply? 330 | # (optional, default: no) 331 | ess_cert_id_chain = no # Must the ESS cert id chain be included? 332 | # (optional, default: no) 333 | ess_cert_id_alg = sha1 # algorithm to compute certificate 334 | # identifier (optional, default: sha1) 335 | 336 | [insta] # CMP using Insta Demo CA 337 | # Message transfer 338 | server = pki.certificate.fi:8700 339 | # proxy = # set this as far as needed, e.g., http://192.168.1.1:8080 340 | # tls_use = 0 341 | path = pkix/ 342 | 343 | # Server authentication 344 | recipient = "/C=FI/O=Insta Demo/CN=Insta Demo CA" # or set srvcert or issuer 345 | ignore_keyusage = 1 # potentially needed quirk 346 | unprotected_errors = 1 # potentially needed quirk 347 | extracertsout = insta.extracerts.pem 348 | 349 | # Client authentication 350 | ref = 3078 # user identification 351 | secret = pass:insta # can be used for both client and server side 352 | 353 | # Generic message options 354 | cmd = ir # default operation, can be overridden on cmd line with, e.g., kur 355 | 356 | # Certificate enrollment 357 | subject = "/CN=openssl-cmp-test" 358 | newkey = insta.priv.pem 359 | out_trusted = insta.ca.crt 360 | certout = insta.cert.pem 361 | 362 | [pbm] # Password-based protection for Insta CA 363 | # Server and client authentication 364 | ref = $insta::ref # 3078 365 | secret = $insta::secret # pass:insta 366 | 367 | [signature] # Signature-based protection for Insta CA 368 | # Server authentication 369 | trusted = insta.ca.crt # does not include keyUsage digitalSignature 370 | 371 | # Client authentication 372 | secret = # disable PBM 373 | key = $insta::newkey # insta.priv.pem 374 | cert = $insta::certout # insta.cert.pem 375 | 376 | [ir] 377 | cmd = ir 378 | 379 | [cr] 380 | cmd = cr 381 | 382 | [kur] 383 | # Certificate update 384 | cmd = kur 385 | oldcert = $insta::certout # insta.cert.pem 386 | 387 | [rr] 388 | # Certificate revocation 389 | cmd = rr 390 | oldcert = $insta::certout # insta.cert.pem 391 | 392 | [pkcs12] 393 | certBagAttr = cb_attr 394 | 395 | # Uncomment this if you need Java compatible PKCS12 files 396 | [cb_attr] 397 | #jdkTrustedKeyUsage = anyExtendedKeyUsage -------------------------------------------------------------------------------- /certs/test.2.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDszCCApsCFHDGB6747rJE8+MWP2IQdEfrONurMA0GCSqGSIb3DQEBCwUAMIGV 3 | MQswCQYDVQQGEwJDTjERMA8GA1UECAwIWmhlamlhbmcxETAPBgNVBAcMCEhhbmd6 4 | aG91MRQwEgYDVQQKDAtWZXNvZnQgSW5jLjEMMAoGA1UECwwDRGV2MRMwEQYDVQQD 5 | DApTaHlsb2NrIEhnMScwJQYJKoZIhvcNAQkBFhhzaHlsb2NrLmh1YW5nQHZlc29m 6 | dC5jb20wHhcNMjMwMTEyMDgyMTA1WhcNMzMwMTA5MDgyMTA1WjCBlTELMAkGA1UE 7 | BhMCQ04xETAPBgNVBAgMCFpoZWppYW5nMREwDwYDVQQHDAhIYW5nemhvdTEUMBIG 8 | A1UECgwLVmVzb2Z0IEluYy4xDDAKBgNVBAsMA0RldjETMBEGA1UEAwwKU2h5bG9j 9 | ayBIZzEnMCUGCSqGSIb3DQEJARYYc2h5bG9jay5odWFuZ0B2ZXNvZnQuY29tMIIB 10 | IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1WFQaXCffwmjwyUJhogVlHdZ 11 | P1PkT/cY6uzQMfiMZibjdsxUrIeNcZU6CGGbS1a+s+BTwuqnOaIWre7qCRtHdzlk 12 | ZaMqFW424WUPCgQxYAekIpw1N8H0gFoj3GqmV+580Ar9Y4L7vUwYwdzMPaFh7AgN 13 | rWDMGOUFXTUW64st2IEIR6M6y0CwFvZMfbIgBrV1oyxfxQnb1OXQQS/Aq0MImstI 14 | fCB+hNhefeGarvJI83O6y0wGXIohTUPKZgkRq+etIw1NzWS3bmCfAt3cwFkvkcMV 15 | GW85OvD1wKCR5vPbTKTs46Nwqoj58+avqwzWvnzYADDRBZBV9/+94ljZ5RbDLwID 16 | AQABMA0GCSqGSIb3DQEBCwUAA4IBAQCxAe7T3POLX16IgQrx416uXunpMBZVEmC/ 17 | 0cr/tX/ZVr1D94pZBDbNBplvrIYM0oGUAlzWuOnGhGONzKMTtsyf1JwG0/I/dg7/ 18 | ng6uhiwDjUuB+dJ+CpoBtFNKr9u+Imw6ApDjcbvaN+ycwHwjBy1XX1SIN5bo+xfk 19 | 2K+dTAxSI1Zlsqs3spGVIc3IqdfUVnbYkNM3Nx3UYxIjgVfVW1WLf7BgsjvStZKF 20 | pqixpST485YibWVxET3GKOJ4/fZwSl3DUGAMIVBZw4l1juZgG2IL5rFB2PPkNybk 21 | 0Xkw4r+RS92NK+CWtfjzQTLGdqxJpLVLt8Ed+q9aA/ugZqT8J3BX 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /certs/test.2.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIC9zCCAd8CAQAwgZUxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzER 3 | MA8GA1UEBwwISGFuZ3pob3UxFDASBgNVBAoMC1Zlc29mdCBJbmMuMQwwCgYDVQQL 4 | DANEZXYxEzARBgNVBAMMClNoeWxvY2sgSGcxJzAlBgkqhkiG9w0BCQEWGHNoeWxv 5 | Y2suaHVhbmdAdmVzb2Z0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC 6 | ggEBALgj6VJTBk7GlbaMW+lwyKjfW+ardnClLbNU+fh0gQGOAffRBz2RiTERzpva 7 | 3gpmG+JuxLOz4mUtcssGYkdX5p2Wpt3r2ne0h15UVth7sovyvmTQrS6opD+IZ9dm 8 | 7lbDxcZBnf+91gXmEB8f9pSc15eIwaUeqGH8s4YiPjVpM7/8VtRfhk5ps4RE2OiW 9 | 1k7vNhxEeBiW9bU5GmY/e8zN4YNJnSo3sXH47KlDekQeA4uZpJkM37GMEmvFImOP 10 | 11OvTIKEbPSuFDEvUKQMDB6xbTe83NUMV7tgMcTzoWbsPH/2GcSVTc32Kf1600tl 11 | as6rl8gU2yrzjesEMOGxnDfFD1sCAwEAAaAcMBoGCSqGSIb3DQEJAjENDAtWZXNv 12 | ZnQgSW5jLjANBgkqhkiG9w0BAQsFAAOCAQEASQzHAVdh3KqljVtxKtKvN2RyKhht 13 | iMYS5YICC7ESLhcZhE4jp/XQ00jYs1tXRIczcmvstY/od9VmPX+ycL4OGUqzRaRF 14 | Y4INCSy2/JIEz8aBoApuFzE5OZe/QS6ihwlZTSVW/Q/lTJeZ2N84Rj6ftlp4ZWLx 15 | 4NtyPuXWXIY/QOHp6rjbUYVcAvhFio2OIgfGFlV5E3rkmb/R2nU3vQ6BNK2sTpt7 16 | 5ovXXbx7UrvS7/YVoP5+fg7iCSPOSA2t9yrD/N5htWSMiUJHp31EaTbNN8yxtVwb 17 | ieAWLAuPrI0xoy6TLh2tDZX4HBw8ZwpBTDyi+tVdJdcsUzRM+w6xoTpjQg== 18 | -----END CERTIFICATE REQUEST----- 19 | -------------------------------------------------------------------------------- /certs/test.2.key: -------------------------------------------------------------------------------- 1 | -----BEGIN ENCRYPTED PRIVATE KEY----- 2 | MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQINtWdG0nk//kCAggA 3 | MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECER/BYBcGn8fBIIEyMtKPrObru3d 4 | AS+3X4f0dSRErHSNUMOaiRe82RmklGX6Jrhn6C7C5nUA7poM+FBrbJYX6j9txcY8 5 | 6MhskOXPtDYq4o7T0VQ0w9GIxS13nPEFz0zdRvimDKJPH5+rUhAv6DTM7YqZAf1D 6 | oQmVPCpWRoYWOtA4NKYZFqv4kIUQQybPclr2vv7Q0pbKdqTvkyK46DRqozgLREj3 7 | iNRIU2xa5nMGrL8M+AjOKTjMJP5fbJXFr1ZezrPRdNg3EWaMAk+9zMfsluYsx6zZ 8 | 60rEHdCStYj4Gisc/FIGDf/CMGiuENrdO6gw5wsZU0MaqbgjUAbCMTngtrAciQiA 9 | Q4bFcu33NOAhcnmiMpBFE3QMHBm1e0aAIR3sSqlJoIdxeD64b6D2+HtHQCvm8dsm 10 | +nDDJVWimbieXQrki0GH/rHZzeDXTkMgMKvjRvpSb/Ksfio9IlPrNu88EMLmN9CC 11 | cxwEPVxJheISyguZwMxlvz4sSsuxz3Msfujc5IJ4t6K4N2LtLqu4LB4WrTMZVGuG 12 | 56rHO2AbrEWKDRywwsm9x6gXppM/IMMKOddUlk02nKxd+oqeQQx2ZwbC2VeydAok 13 | eXbhq8ccus2zZtLE/l6zhM0BCd6eJRp1rrTcx27bSf+KeE/GoYCabi3+ZI4xICW2 14 | pDPSbM0Hg0Ma09FRHYs/Fevmy3VfAZ8VR0zCVvCOku8avfIN5fZBRDaNmu1Fa2CP 15 | WPTkpH3WBlsKyWO73aHVk1h94XI63hPY/5PIQJsgSCg7479nG57nFxjJQCOJzcN1 16 | SjkiXWFmOhLtBrAFc5YAypE+NDDWHKs5EsvUZZ4Xo4uSKtdJDISOZIHqpmp8Ntdp 17 | z2ospT9CGwiVjjefmS6kgwRs+Ky6k6U89hOrdF08LSRM13OCeKQPUu6FuWmqlcBe 18 | U0Pzh4Yxg+VVslbudIfLnlembY6lS5/jp1LFo3tH3wAFG64vdPlpaU16Xqc+rRCw 19 | Ntdjt2v4Ju+S5WImLZO7DOMN93lyg6CYt/yLflMoZdKE7zIAon24U/RjrKn/z0KF 20 | G8I022tll7ahrN7c28M9olzSqEgJMg8+tX1Bn6nxJZuLoTCd2AP47Dzw4rVpYFqh 21 | HAQisNFAzlUntW9TUdMAVHEQY58/dAwZxEWXUMvgizn/3T6eRN4H1zw8BK+ae1ni 22 | nGjlSfhvQJ7CwEhcSwCfYFSN72vkGv42g13CN1bik2aeajqbvS8H4plyHkTzQF44 23 | rqoLdqQTapm0JUU+h1OBApYI/ryM/VQiZr+6onXhlFzc+dl8bmDMpdswM4tWnU+T 24 | BGAyet5DjKanLoz8rWvrm2F+feLbZeepfMK8pI+5mcfZk6B43xjcDYMZcXlt6Crl 25 | 3DyIsVzFGVN7nhtUk1VoQgCOO2aLpa9CjGM6Gb6ugsVqHF7z91XLLHLEc+/qVPWu 26 | UJBXvvnj7H3HBAeOc5Uz1jUdNS+kPPVlQiyQBP++ggpThRL9whsgakSemUy7xtW2 27 | nzYJJhNxkFLEX1fMYucS74+ArI7MFnbuBLbeG/lfjkCl6ErOYCnaIsjK4+Z74Ros 28 | odYQxGYMobvHWzxT12WYUGpbpDslTFqIuULRsRBxeGzF4Ud9e0YI2vnjB4RA5daH 29 | G2KzjFNOtKdl1ng/1UoKIQ== 30 | -----END ENCRYPTED PRIVATE KEY----- 31 | -------------------------------------------------------------------------------- /certs/test.2.password: -------------------------------------------------------------------------------- 1 | vesoft -------------------------------------------------------------------------------- /certs/test.3.CA.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIGATCCA+mgAwIBAgIUd1aWHRmeWlDsLdsqZtKXZlpKJ2kwDQYJKoZIhvcNAQEL 3 | BQAwgY8xCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzERMA8GA1UEBwwI 4 | SGFuZ3pob3UxFDASBgNVBAoMC1Zlc29mdCBJbmMuMQwwCgYDVQQLDANEZXYxDTAL 5 | BgNVBAMMBHJvb3QxJzAlBgkqhkiG9w0BCQEWGHNoeWxvY2suaHVhbmdAdmVzb2Z0 6 | LmNvbTAeFw0yMzAyMjcwMjMyMjRaFw0zMzAyMjQwMjMyMjRaMIGPMQswCQYDVQQG 7 | EwJDTjERMA8GA1UECAwIWmhlamlhbmcxETAPBgNVBAcMCEhhbmd6aG91MRQwEgYD 8 | VQQKDAtWZXNvZnQgSW5jLjEMMAoGA1UECwwDRGV2MQ0wCwYDVQQDDARyb290MScw 9 | JQYJKoZIhvcNAQkBFhhzaHlsb2NrLmh1YW5nQHZlc29mdC5jb20wggIiMA0GCSqG 10 | SIb3DQEBAQUAA4ICDwAwggIKAoICAQCtuncJQZiO3rzr/5dZYIKXe//kbYp187WX 11 | jnrBikqYtK7dy6W5385KV1O+KqbTi6xs9RIFQXSePzrKOhAhP8eDze8aV/jO+aJs 12 | BadSo3EHQNxQXOUQiOC9rMbH0r4mmfsSyYd9ewz7O5RCXbQS1P/pj5YCqhs3qEVf 13 | JHKKnGM/q3itjNCJZinUT70Wa/Pzc3UY04SYfcT+Hcd+xmOYpyNPxZv5hwQAjJhc 14 | DIiCVInjaV4kuIy/aqyW6pox2gjDHmZatXplWvU2vy52pbbow0buJVePijezL3qY 15 | d1SwwoIyK6jW4Wypjy/hnuLavm8DmvVCt6fhxf1W669p4mSpfkUgNVadGvCdZZjM 16 | wvcKOQv9Qy+h4716aIM3ZhrQZJRqRmDJB6PD74ngM9QRCY4EXVNHAbKNNx07vAcX 17 | 6/J4yKEk/5smFaQTUWReHaS+mLu4k4TkGn49tnquDu2fmNhvdT1E9LQqFi4twWpJ 18 | d+F2onFMO4hsh3ggUXu5FEtVuPm5Cf499nbhx8ljZRX9eUGgU2zwk4PFqa/RyN/g 19 | Xzw98O8+ET8VsM8j+zPas+OJpnDS6E3AxiLHRewwY0MDYy6xe8VM93sjDPNYyio1 20 | uiTkxUVUTrzS9ZsP7bv6w4DBAEvgojMNMQvxhTHCatjrxXiUikeX97ZCaPNluIcS 21 | ccE9/WzrLQIDAQABo1MwUTAdBgNVHQ4EFgQUVV8lwkhum8MT7coBaol1nHWf4Icw 22 | HwYDVR0jBBgwFoAUVV8lwkhum8MT7coBaol1nHWf4IcwDwYDVR0TAQH/BAUwAwEB 23 | /zANBgkqhkiG9w0BAQsFAAOCAgEAhs0tnzv8ZxMgNip22w3WGb1Jq3cJTZX1OWrd 24 | sY0iN5xBx3s8yozhhpTP7a/mTjDmQbLEn4KJTuSMW+fHwtM3RwkuG98GwSp8r/ri 25 | ADEZ3uvlC/ZSErSTfCmgZRJ60iNVEHSdGbgMnYtRr7gOmnpI+3lDlzN9HvBVOUmm 26 | QdclvS+4P691bfpoXSLxmtGBZMFMICLddL185rUxeS5fvWVTS+s9D6dvrHb+uHu7 27 | MaKMm3WFs4WDJw1kRI9Ao2UzBGRCOTk1XsersVUuLXPCyGxrsdgaAdiai4qbru/T 28 | o49Mt3aV3PXmKEjw/DQWaCJ7dMzBKnXe4t9777NU2QXOkxnQrdPjQM0d2nr2+dO2 29 | shZJXy9Pd6C1sv2hEfnLI5/ZHM7/tTxzMTSS/1YiX4FPGuFXi1Dek7wOAun22DkQ 30 | BBAnPbnGJZS5Pmg4lWaS/nK0D5IOth15fHWo7J4wjAMMB7BoNkf/2fqgDjSATVQQ 31 | CZN/eTkPWqAjV7/PNj3N0ANRVs3CYJJEEPAoKPJ0SdGUYBRejv/HbJvT8XNRVGvF 32 | GpT+jT144qCCFqFaVF4TBwil+2nstWYfMptUUTbpBNN8aWTMh9auq1T7kDYCXosk 33 | 7jv/+/u3WaATWPyQ9JnqUbOllhEE4//mGnvSHeNtnktmTtLh/y788byOgoXzMrOB 34 | 25ZJsBQ= 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /certs/test.3.CA.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIE1TCCAr0CAQAwgY8xCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzER 3 | MA8GA1UEBwwISGFuZ3pob3UxFDASBgNVBAoMC1Zlc29mdCBJbmMuMQwwCgYDVQQL 4 | DANEZXYxDTALBgNVBAMMBHJvb3QxJzAlBgkqhkiG9w0BCQEWGHNoeWxvY2suaHVh 5 | bmdAdmVzb2Z0LmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK26 6 | dwlBmI7evOv/l1lggpd7/+RtinXztZeOesGKSpi0rt3LpbnfzkpXU74qptOLrGz1 7 | EgVBdJ4/Oso6ECE/x4PN7xpX+M75omwFp1KjcQdA3FBc5RCI4L2sxsfSviaZ+xLJ 8 | h317DPs7lEJdtBLU/+mPlgKqGzeoRV8kcoqcYz+reK2M0IlmKdRPvRZr8/NzdRjT 9 | hJh9xP4dx37GY5inI0/Fm/mHBACMmFwMiIJUieNpXiS4jL9qrJbqmjHaCMMeZlq1 10 | emVa9Ta/LnaltujDRu4lV4+KN7Mveph3VLDCgjIrqNbhbKmPL+Ge4tq+bwOa9UK3 11 | p+HF/Vbrr2niZKl+RSA1Vp0a8J1lmMzC9wo5C/1DL6HjvXpogzdmGtBklGpGYMkH 12 | o8PvieAz1BEJjgRdU0cBso03HTu8Bxfr8njIoST/myYVpBNRZF4dpL6Yu7iThOQa 13 | fj22eq4O7Z+Y2G91PUT0tCoWLi3Bakl34XaicUw7iGyHeCBRe7kUS1W4+bkJ/j32 14 | duHHyWNlFf15QaBTbPCTg8Wpr9HI3+BfPD3w7z4RPxWwzyP7M9qz44mmcNLoTcDG 15 | IsdF7DBjQwNjLrF7xUz3eyMM81jKKjW6JOTFRVROvNL1mw/tu/rDgMEAS+CiMw0x 16 | C/GFMcJq2OvFeJSKR5f3tkJo82W4hxJxwT39bOstAgMBAAGgADANBgkqhkiG9w0B 17 | AQsFAAOCAgEAAcFkuzV65UVAH8+ooYvreQdZR3HWhmMUACI8BBCOQBq3lKNlCffG 18 | F5MEOW7Bc09vUII8IZDDAU9y6cdg+FLSqBRLMQ7H9DrXRAD9vf4djQgrIvXeWmes 19 | AwRGpLBWiJz9OaoPxRiDoH6lYm7OD3bfo0GEC5QaDe+9QNg/rCPa1DBX4GZV0aqB 20 | 6jXUAORhpz52qGGI4ftlqg95sOUk/lcqzsIraNRsiOPrd1MKXT2iwtIYkBRV7gt0 21 | kawki4u+C4FWMd+l7c+Jl5y8r74FMjj8iNzES2MG2FGio9Snf2/nrfw0s1iDTjtS 22 | 5znrGaMWxkMfUIOfcDRn8TbxlWkNCyuOgYfxfLecUEluiIy0WaMr5IT3UedQN0v0 23 | vPpxjNq6vuIKrnwlHaJsRfFK4YTZWVq11xOHlmnUqIzJWTbK2P1hZM3k3UcoA+J9 24 | idmn5+OOuEgT9JwaCppQIgn4fHPU2oEpGnv/k1sAFv0uGs7KFi9y4Lh1DvscBkBT 25 | aJ2Fz87gRANmcQa+xjHLfj6g8PCg2S5SMKtOqCDcvevxwNYK2WQsgw3u2bZw0EV1 26 | AzDMXu8SSOmchndmcAIb5t4vUTrEWPk5DjTq1u/tF5vx+7Hb+RubK1ACxb75RIdj 27 | FZhOQybjb9VfVN0AWUzzC67zwKHvzjcXgufjx5JJnWfr3PexfhdUq/g= 28 | -----END CERTIFICATE REQUEST----- 29 | -------------------------------------------------------------------------------- /certs/test.3.CA.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /certs/test.3.CA.srl: -------------------------------------------------------------------------------- 1 | 1B329AE87F5D174EEC32E952FA9EFD51273A7524 2 | -------------------------------------------------------------------------------- /certs/test.3.derive.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 33 | gz2oV/6v 34 | -----END CERTIFICATE----- 35 | -------------------------------------------------------------------------------- /certs/test.3.derive.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIE2zCCAsMCAQAwgZUxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzER 3 | MA8GA1UEBwwISGFuZ3pob3UxFDASBgNVBAoMC1Zlc29mdCBJbmMuMQwwCgYDVQQL 4 | DANEZXYxEzARBgNVBAMMClNoeWxvY2sgSGcxJzAlBgkqhkiG9w0BCQEWGHNoeWxv 5 | Y2suaHVhbmdAdmVzb2Z0LmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC 6 | ggIBAN1Kpr/5g+dq6sTRmHgx/soSzvAtAU9mM471HK3E33ghPBw7pPfZqtXoLnxZ 7 | YLb9+8f6qvtAcLfJZ4VnKxLHevLrkv7rtYsBeep165WHUbIFIhuphxOMMW6LzBK1 8 | lS3SMx5PDFFpJLnXRRO+BkCLwxRy9wRK8uK/F46Uo/NCuIxxzaudjjMTkdXSBsBj 9 | uNuOFU1vpNmtcnuKdrKlVYU4PGlpUCY4sPSqSSp0NXlexhSZCsTv6JlP+8/Bp4yh 10 | phnPNGAxA52ed15GdiK61kRUC5hMIx9acZwn8M614pS4TxZD0HUDgc1lKmCdUBdU 11 | rZSryXvCJzjpute6JypHzZ7vYwRkvvbns5rJd3Jli9Ym4JnRJ6tpH6f8CLzVb46c 12 | mGd4K3tLOzxz+gFm9MD4wE5nAYxwvIAI5/vu3w6aTdWjkZoHDtcByh+c1CjXchGc 13 | 6prh2cNWSww5iQlGmgXUI6r+yfPXWM566mVXNkBb809+N5sBIp70ByH7KjokLXqe 14 | hMkNUoUHKrA7G6dZH/39OheKI5iM8oc9wj2tfl4D7ZhKqbEc3SEKLUAH5Jc2g6uC 15 | gdV35cZxBe7GOzduMc6sK7I73a6pW1SUSzU3uR02NevJ5wyEuwmCf5ZmI1MewXel 16 | vus0F1OIwYSIHR0YWKimExM36r0n2weZfx+dtzsgBkoCGLSLAgMBAAGgADANBgkq 17 | hkiG9w0BAQsFAAOCAgEACxk1T8FWyqxT6WVjcGi9kwI3MsALoxbdT05eACx7iy9Z 18 | 6RhgQ4rN0w+QlLm0pcKcb9Q/sQeZp5bnUXTfh42WnH/mSLUacLrLRqCHaGPK4O+j 19 | WWZ2jWee3SFeRuN+QuzuQfFxQb/da9yTi0jB1vt8zvdGeBktpKbVkR3Faz8RpJn3 20 | M25QYIMTIi43t4wzQN2kxKr8dhI8SddddgZeYIj8+Ew5fPs90kfsl5XCaINJ/jjG 21 | hz+AWCItsH92nQxUZ6zGExPG5pmZ3LG7pDSlMn17gfhsMGly4/VeLlfTwHeabrl8 22 | VrHn+fTmlifrwZQwXw9SiV1qfpX4QklXZRETU+cY1n9PgZRkX3+7EldZtJgMOIBx 23 | oy9+jdRNMIdmLvaT6rXcTAPWYGnaGyQeXvcoNCj9BDiiZnzAZ+ZLxCakbYIL0q2N 24 | KNz6xASNNd8+sDep1gaG/JmAF/qY1oApJpY2m72n7BhptIrWiRwA5OiAGaAkgIkd 25 | Ke9ugh3urLo+ocAmpEKMJkmFCAvOJPwgN4vSCCBVKpCOR3x2HLLnNFr59S+nAwMk 26 | gTzykUpWMOTmPqz7A4JlW1cPNNX0a/5CDi3OpMXGufW0QG5A6sdQ5xD0Qfe6yesx 27 | nk5qXTHXECg+M2ubZcgeAKDOcZkDWpqNNaCbiGtuwd60rgyRU/giwOGjA7V8g80= 28 | -----END CERTIFICATE REQUEST----- 29 | -------------------------------------------------------------------------------- /certs/test.3.derive.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /certs/test.3.ext: -------------------------------------------------------------------------------- 1 | subjectAltName = @alt_names 2 | [alt_names] 3 | DNS.1 = localhost 4 | IP.1 = 127.0.0.1 -------------------------------------------------------------------------------- /certs/test.ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: DES-EDE3-CBC,6D12ED8559E80FA3 4 | 5 | tv9epnwlt4dP6Q5ee0dACOyFA5BTwYTdoMykQRJrKGwfaNeXUXn+sQ/U/oFHp1Wx 6 | O8VZE+z2aHpiFSTw+Eh6MPt86X5yVG3tpeVO6dErvr8Kd+NpuI8zn7rNoOFRh8wD 7 | 33EFcQMLQPneDl10O18hooIoi0qwp1pd63hYZPwEhB3eOrM5Mnv9OVJs65bzYfyf 8 | Wku33YWYxeqlDvMCsou8PZnv/M2wYsr7+QoTcNmGKP45igMthMDBzwgF+q0p9ZZU 9 | N11c6ojAs01kfuqFf3vKfHNYe6zsBiNhnUuEy8enXSxD5E7tR/OI8aEzPLdk7fmN 10 | /UsMK2LE0Yd5iS3O1x/1ZjSBxJ+M/UzzCO692GTAiD6Hc13iJOavq/vt1mEPjfCD 11 | neF38Bhb5DfFi+UAHrz6EHMreamGCzP82us2maIs7mSTq7nXDZfbBc7mBDLAUUnT 12 | J6tlrTyc+DQXzkJa6jmbxJhcsWm6XvjIBEzSXVHxEDPLnZICQk3VXODjCXTD75Rg 13 | 0WaS78Ven7DW8wn07q3VzWAFDKaet3VI+TVTv7EfIavlfiA6LSshaENdFLeHahNE 14 | s/V/j5K3Pg6+WQcZRgOsfqIwUCSQxY13R6TTdaaCkLay5BggF5iiAO3pkqsJiadf 15 | w843Ak4USBptymJxoZgJyFtQHpQyNiFfsAbs9BaYbg2evvE7/VQhLk0gQ7HgQMeJ 16 | wgxEQqZQKDCCSugSzY1YEGXKnrZYCKyipzyyH936mE15zNwhYp/Pi2020+gmtP3h 17 | CDfcPs1yeLI2/1JuimafbuKsv9xchWa6ASU8p8Q7wTLtUj9ylLKyA4A/75pK0DXG 18 | Hv/q0O+UfhAMD438SoPBle7RSvIsDU1VjUqstlNybBglBZxGIME7/18+Ms7U32wh 19 | 4xFkZwxT2nqFgyk37tXMdMz9UBh12/AXR9NU4XY37C3Ao2TDT7/0DvU6KdJhsDpv 20 | rGcaC2zzhko+0CPrLlk52KbqP003JXiWvOSI+FylyPPDB/YGitmndJUuQblf3u/E 21 | l+tGi9MeSBQeWKV6D3AVnO05AZjfTUzSK0vw4DgNh5YPNJvLy31B7kDAS88vyGI1 22 | t6MBwjW4/tz/nS/p1Go3mSzBhPkIsCrZE+ar7lH8p8JqkLl4fXIMaVKIfyfJdzyS 23 | lkh3K7bOGDPegxxxaWdb+EnC7k+1R3EOU7uJFW61HyrGI3q6Y7kOl5aYSJ5Ge1Uv 24 | PycFWHWVTHq/R7HRE6HIJzGe/PnLIbStXLDFeivjfcYq1YaSaF8Vl+xg+0u3ULOl 25 | P6IuPTph6dlcgttRZVl3ETcF0T+2wfbUwgjf0ZiguCJfR2jLGhPl1KBg0Kd9cTSY 26 | zI3YMMd2G8hApt/QFlm4Ry8CqaJUmDcjDNIJT3M+RldUgfz37NsX05cA5e9+I1AL 27 | 2406F/v5U9gWsYx7HuwJtQrDzYYDbl1GD4H+qHFJE5JYhPP4AyWYxJ1NR5dqyvrt 28 | +3r5+xlwZrS76c10RsBWL7th8ZEzRxOZxbtLwbf4bG/tIGfQP2sTnWwA+qym6b2S 29 | sRduqOTP+xwnhOq/ZKn8lfsDfhT8CPnKHBsd09kM9y/UWuxFe0upLydRLE/Wsb9s 30 | -----END RSA PRIVATE KEY----- 31 | -------------------------------------------------------------------------------- /certs/test.ca.password: -------------------------------------------------------------------------------- 1 | vesoft -------------------------------------------------------------------------------- /certs/test.ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEGzCCAwOgAwIBAgIUDcmZFpL4PcdCXfLRBK8bR2vb39cwDQYJKoZIhvcNAQEL 3 | BQAwgZwxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzERMA8GA1UEBwwI 4 | SGFuZ3pob3UxFDASBgNVBAoMC1Zlc29mdCBJbmMuMRAwDgYDVQQLDAdzZWN0aW9u 5 | MRYwFAYDVQQDDA1zaHlsb2NrIGh1YW5nMScwJQYJKoZIhvcNAQkBFhhzaHlsb2Nr 6 | Lmh1YW5nQHZlc29mdC5jb20wHhcNMjEwODE5MDkyNDQ3WhcNMjUwODE4MDkyNDQ3 7 | WjCBnDELMAkGA1UEBhMCQ04xETAPBgNVBAgMCFpoZWppYW5nMREwDwYDVQQHDAhI 8 | YW5nemhvdTEUMBIGA1UECgwLVmVzb2Z0IEluYy4xEDAOBgNVBAsMB3NlY3Rpb24x 9 | FjAUBgNVBAMMDXNoeWxvY2sgaHVhbmcxJzAlBgkqhkiG9w0BCQEWGHNoeWxvY2su 10 | aHVhbmdAdmVzb2Z0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB 11 | AMEAgpamCQHl+8JnUHI6/VmJHjDLYJLTliN/CwpFrhMqIVjJ8wG57WYLpXpn91Lz 12 | eHu52LkVzcikybIJ2a+LOTvnhNFdbmTbqDtrb+s6wM/sO+nF6tU2Av4e5zhyKoeR 13 | LL+rHMk3nymohbdN4djySFmOOU5A1O/4b0bZz4Ylu995kUawdiaEo13BzxxOC7Ik 14 | Gge5RyDcm0uLXZqTAPy5Sjv/zpOyj0AqL1CJUH7XBN9OMRhVU0ZX9nHWl1vgLRld 15 | J6XT17Y9QbbHhCNEdAmFE5kEFgCvZc+MungUYABlkvoj86TLmC/FMV6fWdxQssyd 16 | hS+ssfJFLaTDaEFz5a/Tr48CAwEAAaNTMFEwHQYDVR0OBBYEFK0GVrQx+wX1GCHy 17 | e+6fl4X+prmYMB8GA1UdIwQYMBaAFK0GVrQx+wX1GCHye+6fl4X+prmYMA8GA1Ud 18 | EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAHqP8P+ZUHmngviHLSSN1ln5 19 | Mx4BCkVeFRUaFx0yFXytV/iLXcG2HpFg3A9rAFoYgCDwi1xpsERnBZ/ShTv/eFOc 20 | IxBY5yggx3/lGi8tAgvUdarhd7mQO67UJ0V4YU3hAkbnZ8grHHXj+4hfgUpY4ok6 21 | yaed6HXwknBb9W8N1jZI8ginhkhjaeRCHdMiF+fBvNCtmeR1bCml1Uz7ailrpcaT 22 | Mf84+5VYuFEnaRZYWFNsWNCOBlJ/6/b3V10vMXzMmYHqz3xgAq0M3fVTFTzopnAX 23 | DLSzorL/dYVdqEDCQi5XI9YAlgWN4VeGzJI+glkLOCNzHxRNP6Qev+YI+7Uxz6I= 24 | -----END CERTIFICATE----- 25 | -------------------------------------------------------------------------------- /certs/test.ca.srl: -------------------------------------------------------------------------------- 1 | 4AF2EBB941EA7EE8358ECC7E51C2F1A38EE18873 2 | -------------------------------------------------------------------------------- /certs/test.derive.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDvjCCAqYCFEry67lB6n7oNY7MflHC8aOO4YhzMA0GCSqGSIb3DQEBCwUAMIGc 3 | MQswCQYDVQQGEwJDTjERMA8GA1UECAwIWmhlamlhbmcxETAPBgNVBAcMCEhhbmd6 4 | aG91MRQwEgYDVQQKDAtWZXNvZnQgSW5jLjEQMA4GA1UECwwHc2VjdGlvbjEWMBQG 5 | A1UEAwwNc2h5bG9jayBodWFuZzEnMCUGCSqGSIb3DQEJARYYc2h5bG9jay5odWFu 6 | Z0B2ZXNvZnQuY29tMB4XDTIxMDgyNDEwNTExMloXDTIzMTEyNzEwNTExMlowgZkx 7 | CzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzERMA8GA1UEBwwISGFuZ3po 8 | b3UxFDASBgNVBAoMC1Zlc29mdCBJbmMuMRAwDgYDVQQLDAdzZWN0aW9uMRMwEQYD 9 | VQQDDApTaHlsb2NrIEhnMScwJQYJKoZIhvcNAQkBFhhzaHlsb2NrLmh1YW5nQHZl 10 | c29mdC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHk1PQtaCG 11 | S31nvxKuT6pzVQuOsA2hEIDzBZuoBK3blezBB16fjUWG2wHG/r9Oss5YzOly4viL 12 | 1oFLsNdYg27EFH7pcGfdSUmZa6LHILegJTmLa1aB4lRG9EsvPIxNuo637CW2z6EW 13 | ElVKXn2N1G1vW3fpKGxJ+d1ovaFfBliO0sK+myW+vYdKrNg70WqKKCoCIlIjEWw3 14 | vQdrmvhuhIBbG1bXkXbJwIepBdb4wGSx8qsgs93I6/je/K/iJaPJIqdH8loo6fSo 15 | DBUiNA87ZsQdtbBeuk7QuF71SxD5+E8wCMtFMwRGmL0vYMPwkaurKxwEs49e8eTz 16 | RvIrNtyYgVo7AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGBpm5OLXn02kWr1ENU5 17 | FOOVryD41SCmPy8hLwQ2MCXd446UfTXc5TTlllksaePn373ZANLUe78vUCoVPjOh 18 | dU5GxyOKtubXovI+yuvMS11u00KtgiAd5qa+IhX3c/P60bh4+fdKZ9ViyLsG+IpQ 19 | +XDYT2uekLyjXXJU6h1raW7M1VY9FcDC63moXz0WgWJ/9tJgB0ZQkVcL+2UpveoZ 20 | Whf9P0xAzCmNSrR7CMhdeRN2vBQQaHXk/64wkHncdkz/NglVl00rh4MtBKZ6Cqze 21 | uZvgrxOJNzB4aXBMHO7sWzw1VSfS79CZm4H39hBWGiVEkr3yZYQbboDRY6F5dQyc 22 | BZc= 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /certs/test.derive.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIDEjCCAfoCAQAwgZkxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzER 3 | MA8GA1UEBwwISGFuZ3pob3UxFDASBgNVBAoMC1Zlc29mdCBJbmMuMRAwDgYDVQQL 4 | DAdzZWN0aW9uMRMwEQYDVQQDDApTaHlsb2NrIEhnMScwJQYJKoZIhvcNAQkBFhhz 5 | aHlsb2NrLmh1YW5nQHZlc29mdC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw 6 | ggEKAoIBAQDHk1PQtaCGS31nvxKuT6pzVQuOsA2hEIDzBZuoBK3blezBB16fjUWG 7 | 2wHG/r9Oss5YzOly4viL1oFLsNdYg27EFH7pcGfdSUmZa6LHILegJTmLa1aB4lRG 8 | 9EsvPIxNuo637CW2z6EWElVKXn2N1G1vW3fpKGxJ+d1ovaFfBliO0sK+myW+vYdK 9 | rNg70WqKKCoCIlIjEWw3vQdrmvhuhIBbG1bXkXbJwIepBdb4wGSx8qsgs93I6/je 10 | /K/iJaPJIqdH8loo6fSoDBUiNA87ZsQdtbBeuk7QuF71SxD5+E8wCMtFMwRGmL0v 11 | YMPwkaurKxwEs49e8eTzRvIrNtyYgVo7AgMBAAGgMzAVBgkqhkiG9w0BCQcxCAwG 12 | dmVzb2Z0MBoGCSqGSIb3DQEJAjENDAtWZXNvZnQgSW5jLjANBgkqhkiG9w0BAQsF 13 | AAOCAQEAjmyCyxziJMR8NILRAwmfYcBB90CbTFMMEyWy402KxoXcyVZBGO2eukIq 14 | gaF2ywuh6yuTPtGsdVMVTWDQ4RLYpoQoR5Blu+M8Or8rhZSfMYXi79Ne3abSF28E 15 | eWjBmh2Ys0GtaThlufJBWE+vWPH2iEGrSRTg1fvBLBzAW6nXU2svoTrKfDcEoY5z 16 | xB0CKhBoewoIZ2FPBmBAnIWHfXR/vQ76QIoNdfQ4nT8iXuLRoNjRlvVU4AUDwKtu 17 | keRDrnmJ7A5eqTlleCMzra2MAp9Na9gojXlGQP9q9V8nFtSvbjYAoH0ezWpdWj4+ 18 | Rtu9EK4JkDymmmZcneFapExZrRLt0A== 19 | -----END CERTIFICATE REQUEST----- 20 | -------------------------------------------------------------------------------- /certs/test.derive.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAx5NT0LWghkt9Z78Srk+qc1ULjrANoRCA8wWbqASt25XswQde 3 | n41FhtsBxv6/TrLOWMzpcuL4i9aBS7DXWINuxBR+6XBn3UlJmWuixyC3oCU5i2tW 4 | geJURvRLLzyMTbqOt+wlts+hFhJVSl59jdRtb1t36ShsSfndaL2hXwZYjtLCvpsl 5 | vr2HSqzYO9FqiigqAiJSIxFsN70Ha5r4boSAWxtW15F2ycCHqQXW+MBksfKrILPd 6 | yOv43vyv4iWjySKnR/JaKOn0qAwVIjQPO2bEHbWwXrpO0Lhe9UsQ+fhPMAjLRTME 7 | Rpi9L2DD8JGrqyscBLOPXvHk80byKzbcmIFaOwIDAQABAoIBAEZ50URHjzs9VziW 8 | sdsaSN/XbXBi3T0+Xbr0BQatOFPtuqBjoNeJBL9dgWArP5Vj8RhMrDekzQ5cnmYD 9 | OdiI+UmGz1ZSGmt7YOErsFzPQejsnEiOjArryMURqacxo34jXhi27I6E/aaUrMfJ 10 | XF8EX+zOCSct3ie1c6l0JZMv43/zbzP2vMFEdfnVfZA2Kxo5l3I4rjuxHUEWHzrb 11 | EgM4a2+y7LQrut75zP9zWEZAqim/VEIEj24Gqj+Vocb6cHlc31KzKaEz7Ra5ha2J 12 | kN2CQRKCzoMupVL5E6dWMiDVjUyUXdUgjSCIW2H+E1ONgvxA78jJx7+Dzj+/bWxH 13 | h/vr3dkCgYEA9Aev7PGoGF0eapZY3crehvtCn1v4YLheh0dk4EpbpbEx0rQaG3h7 14 | YYCf7euxMvoTsKPETHAUG/s/RZV1DNOjxs8GKgEIVaRYEf1VZeDXudtnyKBwCMAL 15 | 5CKHRBvfmNG9n+PpQQlrIAZGej7HU+/IzEVsrD2A5DeH9IVpMNvrX10CgYEA0V1r 16 | aydbBP+Ma/fiG5UDa8l4GdLzvAoW2cY6ZhQX4NiLTK91MwA/QOQcVMvJAN2KpPHC 17 | kGDRT7IhMs66cMxl0ImIJ2QSnv8HRNmBBSdUtJx1S6nV2u0VfgP61oNT/YbLR/Jk 18 | CAIl1qe7Q8IsrMbPxCbt8g+D8Wr9C3pdYYqFvncCgYEAicGdKmDwx3Apr3nYCLxx 19 | CjnkzhkZCWCK3EsNQyA2xD5XJd7NrhxBajU2ExUuHtzVKK4KLixG7dTTTvCj9u2y 20 | UpSjoiqbDd2MaftcrfpTTXPyDmujUw02qT5kpaomexpLtWrvTeuHMbjZKEEwPM3r 21 | yISYaFL/49UFRp/ZVd+P63ECgYAX1B0ctf77A6bUxwK6Buy7wNNlhQful+tf39rX 22 | sWPCWIMKOFILevS4Cv5afFMlQRG9kjKFwi8wdeKnaLX5jpnr8StI6G/iHr6SDHtN 23 | vds7Ly9+bBcF8sPmcseC0LGngkbyqljOPIhX9QEwRhJVm88b0R511WQ7/uRMASJN 24 | rrloIwKBgCxYlu1xvvEuQNoIux/yKAEJ1h4Ta2zc5upjw0uDKMi0UNIbNhgdFOvj 25 | LuVbxTRU8WktrLNk3T0rsopKsTbEZVg6Yuv8ZLkEiNYTzhUbn2Y5yM3bnoVwyOns 26 | pTtqmBtvDZxaRCYdIQG3b09IvrewDk26AOtNHdeKw883G2muP/vA 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /certs/test.nopassword.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDtTCCAp0CFA6+GFOxrvZfJFWzyeyOgMXcPcf8MA0GCSqGSIb3DQEBCwUAMIGW 3 | MQswCQYDVQQGEwJDTjERMA8GA1UECAwIWmhlamlhbmcxETAPBgNVBAcMCEhhbmd6 4 | aG91MRQwEgYDVQQKDAtWZXNvZnQgSW5jLjENMAsGA1UECwwERGV2LjETMBEGA1UE 5 | AwwKU2h5bG9jayBIZzEnMCUGCSqGSIb3DQEJARYYc2h5bG9jay5odWFuZ0B2ZXNv 6 | ZnQuY29tMB4XDTIzMDIxNzA1NTY1MloXDTI3MTIwMzA1NTY1MlowgZYxCzAJBgNV 7 | BAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzERMA8GA1UEBwwISGFuZ3pob3UxFDAS 8 | BgNVBAoMC1Zlc29mdCBJbmMuMQ0wCwYDVQQLDAREZXYuMRMwEQYDVQQDDApTaHls 9 | b2NrIEhnMScwJQYJKoZIhvcNAQkBFhhzaHlsb2NrLmh1YW5nQHZlc29mdC5jb20w 10 | ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDP2aiw//Ov6Sza/+4Or2aj 11 | /M8Co1w2Ij2bVWSzKW1ubypnyhzEEsRcvT4Lict/hibvMl8PG+RBJ41xU4a8xVPv 12 | jBOlUW1aTb4ohwmk+sIVzqmcSRRmm+lb1VqMhnsmsVSJRo/KnlO/ebqUPTFEFeDR 13 | Pa8z1nnIqT7jQPkyb5nA2GbNUehie3ieVsFnOLBtgtizXJrTgmrMIcKFVRRMF6Jw 14 | tIFcYI7Cnix4StX4UcwvkEzzdB5rIMFxCCnzhBZOMcW0Pxgb5hiwC6xDIMTG8ekr 15 | pdo+FZV6i1G/v/8XgGBR8nTXgCrFz2JrtpbgsX+PpE/j3l3jzY7+nx3eNwyLGt9r 16 | AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEW/GdTkvrBlsyEnNh6QjV4GeB07lVc8 17 | jUyvf6lnGkVSInJcMtgOk6++VhtKQ4tusxlNTb8qlPC3WeQst65F5UMp4hr9ULga 18 | fE7tm2KGrdJS0eu7hqTkahr+QtM2vZ2Rkvmp2+M2i6bk9jSnGB3eIGILsQqf4udt 19 | IHlvWPasI6S4PJ3VzWFxekj1shaCtotK4nDzBriHxZbwC2mi8NbDC3I7uP62BDeI 20 | qX5rSwfchH2W3hL0kkT0S6xuBARq8Vl4W7Hfd1AxqJbKXQxNLMeONq8i9SU21KGe 21 | xxNb2I9JyJ31CerZ9KUrxOZxMWvgVcY0UvgADt4pk9o4HAHDPrUGwfo= 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /certs/test.nopassword.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIC3DCCAcQCAQAwgZYxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzER 3 | MA8GA1UEBwwISGFuZ3pob3UxFDASBgNVBAoMC1Zlc29mdCBJbmMuMQ0wCwYDVQQL 4 | DAREZXYuMRMwEQYDVQQDDApTaHlsb2NrIEhnMScwJQYJKoZIhvcNAQkBFhhzaHls 5 | b2NrLmh1YW5nQHZlc29mdC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 6 | AoIBAQCdMNLt8nCJV64d+G/5sBnWBkMx+dwjlcVuvR5h4rHB5bkqyNWmpT/UY1EU 7 | T2pcximZtzhHP1yKFDh4l362qsQQ+C1ICLXOt45mwqvfoDpT7XDN3PHa+q/d7dZO 8 | 8d2oiy4aSAB/OF9DfqVc8MSdyIbozvdZkINOPfjG3Q4w3tck6YIcP7vRlP+uKfSv 9 | zh0o1oEfEEqGkSo0GJ9AjoY6OTU6ryEs9+EtwCS5NBkKoY4GSwB9U/erxIyUZeql 10 | qHNBpFBKuZ5tQ5Dn0WM6iMflGYZwMUVGipMR7dFFb/cDW0wEYIX2evOcR1AUIrv3 11 | I5AXZ7LWeyIqQ3kZ1iHFqi9XDl8PAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEA 12 | ZFIKiZEKsWslMlXRJ0pYc7PVpxhMY49t2kw4ekXY49JBwsYRnUpMpWTIbAcQGHI+ 13 | 7wPCGXdpJWSKLSGMgyHfo5csuVbtHHmjwzwD/UwiIxg3Gx5jd5Giewh0fc2Chzzs 14 | nnCNkYSe3dsjhuzwntx0wXzWjm+OpGdEPOVm6jtwaRdj5tr8kQ+yJRvLtdQGV4C8 15 | Xr1Bsx3DU/iffcykL2AJK33t5FoiFNUIEsOzJgrO1E/O9tgJDsHmwE8rxuCqdR89 16 | FLbdHQc3M+PkdN7VHc/+bFNUwy2BZcrtc90k0MNwdUJnTiCOApW3898rE6V7d8bf 17 | SEHdF/kM4x+hNlxojK3HNg== 18 | -----END CERTIFICATE REQUEST----- 19 | -------------------------------------------------------------------------------- /certs/test.nopassword.derive.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDtTCCAp0CFCWUTayDNNCINKoA6DkurIxgXzhnMA0GCSqGSIb3DQEBCwUAMIGW 3 | MQswCQYDVQQGEwJDTjERMA8GA1UECAwIWmhlamlhbmcxETAPBgNVBAcMCEhhbmd6 4 | aG91MRQwEgYDVQQKDAtWZXNvZnQgSW5jLjENMAsGA1UECwwERGV2LjETMBEGA1UE 5 | AwwKU2h5bG9jayBIZzEnMCUGCSqGSIb3DQEJARYYc2h5bG9jay5odWFuZ0B2ZXNv 6 | ZnQuY29tMB4XDTIzMDIxNzA2MDQwOVoXDTI3MTIwMzA2MDQwOVowgZYxCzAJBgNV 7 | BAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzERMA8GA1UEBwwISGFuZ3pob3UxFDAS 8 | BgNVBAoMC1Zlc29mdCBJbmMuMQ0wCwYDVQQLDAREZXYuMRMwEQYDVQQDDApTaHls 9 | b2NrIEhnMScwJQYJKoZIhvcNAQkBFhhzaHlsb2NrLmh1YW5nQHZlc29mdC5jb20w 10 | ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCdMNLt8nCJV64d+G/5sBnW 11 | BkMx+dwjlcVuvR5h4rHB5bkqyNWmpT/UY1EUT2pcximZtzhHP1yKFDh4l362qsQQ 12 | +C1ICLXOt45mwqvfoDpT7XDN3PHa+q/d7dZO8d2oiy4aSAB/OF9DfqVc8MSdyIbo 13 | zvdZkINOPfjG3Q4w3tck6YIcP7vRlP+uKfSvzh0o1oEfEEqGkSo0GJ9AjoY6OTU6 14 | ryEs9+EtwCS5NBkKoY4GSwB9U/erxIyUZeqlqHNBpFBKuZ5tQ5Dn0WM6iMflGYZw 15 | MUVGipMR7dFFb/cDW0wEYIX2evOcR1AUIrv3I5AXZ7LWeyIqQ3kZ1iHFqi9XDl8P 16 | AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAH5HwRQQfbXKpSLi8v6lcd44VFn/aQwr 17 | 3AfAitukcRgYzGDr+NnZq+di0HxZqnxnUQMNAdwTD8eWaqhEVbvE/G1JHKQOKy2t 18 | pMHaNfwV2GEAvZNMKLuXfdFb33F27w7FBLkNbDoGwJbkY9rwmfqOLrrgqvuIZtI1 19 | 0aF/X3pv3Jefjr1l/2rrdez+TizzyTHUv9MkP/mT9DwMKxpeOA5oPD9UdQGae7jm 20 | 4T+ekSaw/rFGSQft9CQGGA0DdP6Pq4R60FXAP2Ix4oKeTR+5IK4tO4wqayVkVkvG 21 | iIPvIa6WU/Je40ab6b6W4NzJ0+KadEJrcwwtbj+eYArhGR2+GYvZ/5Y= 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /certs/test.nopassword.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDP2aiw//Ov6Sza 3 | /+4Or2aj/M8Co1w2Ij2bVWSzKW1ubypnyhzEEsRcvT4Lict/hibvMl8PG+RBJ41x 4 | U4a8xVPvjBOlUW1aTb4ohwmk+sIVzqmcSRRmm+lb1VqMhnsmsVSJRo/KnlO/ebqU 5 | PTFEFeDRPa8z1nnIqT7jQPkyb5nA2GbNUehie3ieVsFnOLBtgtizXJrTgmrMIcKF 6 | VRRMF6JwtIFcYI7Cnix4StX4UcwvkEzzdB5rIMFxCCnzhBZOMcW0Pxgb5hiwC6xD 7 | IMTG8ekrpdo+FZV6i1G/v/8XgGBR8nTXgCrFz2JrtpbgsX+PpE/j3l3jzY7+nx3e 8 | NwyLGt9rAgMBAAECggEBAJxm+bQu9Y0iVfl5agYs4Hx3iMJAcOvfOiJ/0GWsv8bA 9 | 6dTt1lWIla//SwfdqX6NURDfA37YCxsoCxwCsTms9usjqcjVjAbOx0+xxYUamB9W 10 | wnRy3WIU4jWTRy7FJzhHagheS2c5WmWT0N0pasAEAocQ9px4QG3JRSQ2SQ41winE 11 | kZsYx9vURAzCVaSBxVRzHtNtuAfGaTSXIhLBeFJIVJ9YrDYbkkoAi+cZ4L4SLRz+ 12 | yBJdvVoOb+PGuDQbkTFDbVbJLizE0jQg0y/Zphu2HHTsvifjrJ1/lqOfwX41eX6b 13 | ZpKsXFiwtCBhURgvwKcFdZbKx+e3XB3Z5w7OlyQOblkCgYEA8WeKxhQnPq0WMzYz 14 | /KStUpLSIxmNXFISccCG4aS6yTNT7G8BA79G8RdAZAJGezDZez30c/iZemqktRTD 15 | IctowHrAhqM353xMrr1cI05obPs8fOLt/D+ZCM50kTbltie+IaUfAM7mD/ma0hhj 16 | qZX5ezQB10Tyt934WkhcU5ti0+0CgYEA3GrDt+59bHZUZiSQDwcUFKbPwEuwvylk 17 | rhRnO0xak0vakJ/8MtLHWFOaJYGviqmHR1AgpNOSgZBoI5anzXbTrgnk8+/SYoNI 18 | ws6269TtbHRjY6ehkQMG0f3+yXQ9Xxh+Ku8hAxEy8DFoxXG83UPHw0+caXRJSuoS 19 | aFJjIofgxbcCgYEA1rXTVk5CgZqNbks9fe4yQolX4eQuOPi+cvJchYFwqNfAQ0CP 20 | 1Hdib7jQiqMDcTqHlPgL3sI0MA9+I5kt3Xb/2oLTjmzJoliKE7lUpApvEs96g1GL 21 | fppKUOzf/PCo65T5pQ8qPhvHwFmvfEI5EnLUDOK66G7ouuTwqcxGYg6InhkCgYEA 22 | vbDAST3dAQgA8HrM47HyzScWKea58HoeJFZ41SaL2LHyIn8c21xcpJ4mTdsDCLGH 23 | M4842MEgF5MAnIgeV4vI9sGbIXgmNhPH/adK80CnJ6wukOgF+reBGh2eNR9tZba9 24 | dhOf9HTLwryy7yFIKaAYffS4pmSmYut49gDZQjFv9pcCgYEA4keCwbO/dZBy/mvC 25 | 38QymWHS8Wh7U9CocDD1VrEOaxqw/2dIe2H2FSMJVkKbCFp7OmhsX7bGQ7FGil/E 26 | /TesiT1N/moGQfuAwv9ysQfARN98bw1g+tiwrDgmYxSHM3a6WqWvUINGEtGnNIAT 27 | IOffwmfqpMgxhaAHQ+69Wv6xvBY= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /certs/test.nopassword.srl: -------------------------------------------------------------------------------- 1 | 25944DAC8334D08834AA00E8392EAC8C605F3867 2 | -------------------------------------------------------------------------------- /certs/test.password: -------------------------------------------------------------------------------- 1 | vesoft -------------------------------------------------------------------------------- /configs.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2020 vesoft inc. All rights reserved. 4 | * 5 | * This source code is licensed under Apache 2.0 License. 6 | * 7 | */ 8 | 9 | package nebula_go 10 | 11 | import ( 12 | "crypto/tls" 13 | "crypto/x509" 14 | "fmt" 15 | "io" 16 | "net/http" 17 | "os" 18 | "time" 19 | ) 20 | 21 | // PoolConfig is the configs of connection pool 22 | type PoolConfig struct { 23 | // Socket timeout and Socket connection timeout, unit: seconds 24 | TimeOut time.Duration 25 | // The idleTime of the connection, unit: seconds 26 | // If connection's idle time is longer than idleTime, it will be delete 27 | // 0 value means the connection will not expire 28 | IdleTime time.Duration 29 | // The max connections in pool for all addresses 30 | MaxConnPoolSize int 31 | // The min connections in pool for all addresses 32 | MinConnPoolSize int 33 | // UseHTTP2 indicates whether to use HTTP2 34 | UseHTTP2 bool 35 | // HttpHeader is the http headers for the connection when using HTTP2 36 | HttpHeader http.Header 37 | // client handshakeKey, make sure the client handshakeKey is in the white list of NebulaGraph server 'client_white_list' 38 | HandshakeKey string 39 | } 40 | 41 | // validateConf validates config 42 | func (conf *PoolConfig) validateConf(log Logger) { 43 | if conf.TimeOut < 0 { 44 | conf.TimeOut = 0 * time.Millisecond 45 | log.Warn("Illegal Timeout value, the default value of 0 second has been applied") 46 | } 47 | if conf.IdleTime < 0 { 48 | conf.IdleTime = 0 * time.Millisecond 49 | log.Warn("Invalid IdleTime value, the default value of 0 second has been applied") 50 | } 51 | if conf.MaxConnPoolSize < 1 { 52 | conf.MaxConnPoolSize = 10 53 | log.Warn("Invalid MaxConnPoolSize value, the default value of 10 has been applied") 54 | } 55 | if conf.MinConnPoolSize < 0 { 56 | conf.MinConnPoolSize = 0 57 | log.Warn("Invalid MinConnPoolSize value, the default value of 0 has been applied") 58 | } 59 | } 60 | 61 | // GetDefaultConf returns the default config 62 | func GetDefaultConf() PoolConfig { 63 | return PoolConfig{ 64 | TimeOut: 0 * time.Millisecond, 65 | IdleTime: 0 * time.Millisecond, 66 | MaxConnPoolSize: 10, 67 | MinConnPoolSize: 0, 68 | UseHTTP2: false, 69 | HandshakeKey: "", 70 | } 71 | } 72 | 73 | // GetDefaultSSLConfig reads the files in the given path and returns a tls.Config object 74 | func GetDefaultSSLConfig(rootCAPath, certPath, privateKeyPath string) (*tls.Config, error) { 75 | rootCA, err := openAndReadFile(rootCAPath) 76 | if err != nil { 77 | return nil, err 78 | } 79 | cert, err := openAndReadFile(certPath) 80 | if err != nil { 81 | return nil, err 82 | } 83 | privateKey, err := openAndReadFile(privateKeyPath) 84 | if err != nil { 85 | return nil, err 86 | } 87 | clientCert, err := tls.X509KeyPair(cert, privateKey) 88 | if err != nil { 89 | return nil, err 90 | } 91 | // parse root CA pem and add into CA pool 92 | // for self-signed cert, use the local cert as the root ca 93 | rootCAPool := x509.NewCertPool() 94 | ok := rootCAPool.AppendCertsFromPEM(rootCA) 95 | if !ok { 96 | return nil, fmt.Errorf("unable to append supplied cert into tls.Config, please make sure it is a valid certificate") 97 | } 98 | return &tls.Config{ 99 | Certificates: []tls.Certificate{clientCert}, 100 | RootCAs: rootCAPool, 101 | }, nil 102 | } 103 | 104 | func openAndReadFile(path string) ([]byte, error) { 105 | // open file 106 | f, err := os.Open(path) 107 | if err != nil { 108 | return nil, fmt.Errorf("unable to open file %s: %s", path, err) 109 | } 110 | // read file 111 | b, err := io.ReadAll(f) 112 | if err != nil { 113 | return nil, fmt.Errorf("unable to ReadAll file %s: %s", path, err) 114 | } 115 | return b, nil 116 | } 117 | 118 | // SessionPoolConf is the configs of a session pool 119 | // Note that the space name is bound to the session pool for its lifetime 120 | type SessionPoolConf struct { 121 | username string // username for authentication 122 | password string // password for authentication 123 | serviceAddrs []HostAddress // service addresses for session pool 124 | hostIndex int // index of the host in ServiceAddrs that the next new session will connect to 125 | spaceName string // The space name that all sessions in the pool are bound to 126 | sslConfig *tls.Config // Optional SSL config for the connection 127 | retryGetSessionTimes int // The max times to retry get new session when executing a query 128 | 129 | // Basic pool configs 130 | // Socket timeout and Socket connection timeout, unit: seconds 131 | timeOut time.Duration 132 | // The idleTime of the connection, unit: seconds 133 | // If connection's idle time is longer than idleTime, it will be delete 134 | // 0 value means the connection will not expire 135 | idleTime time.Duration 136 | // The max sessions in pool for all addresses 137 | maxSize int 138 | // The min sessions in pool for all addresses 139 | minSize int 140 | // useHTTP2 indicates whether to use HTTP2 141 | useHTTP2 bool 142 | // httpHeader is the http headers for the connection 143 | httpHeader http.Header 144 | // client handshakeKey, make sure the client handshakeKey is in the white list of NebulaGraph server 'client_white_list' 145 | handshakeKey string 146 | } 147 | 148 | type SessionPoolConfOption func(*SessionPoolConf) 149 | 150 | // NewSessionPoolConfOpt creates a new NewSessionPoolConf with the provided options 151 | func NewSessionPoolConf( 152 | username, password string, 153 | serviceAddrs []HostAddress, 154 | spaceName string, opts ...SessionPoolConfOption) (*SessionPoolConf, error) { 155 | // Set default values for basic pool configs 156 | newPoolConf := SessionPoolConf{ 157 | username: username, 158 | password: password, 159 | serviceAddrs: serviceAddrs, 160 | spaceName: spaceName, 161 | retryGetSessionTimes: 1, 162 | timeOut: 0 * time.Millisecond, 163 | idleTime: 0 * time.Millisecond, 164 | maxSize: 30, 165 | minSize: 1, 166 | hostIndex: 0, 167 | } 168 | 169 | // Iterate the given options and apply them to the config. 170 | for _, overwrite := range opts { 171 | overwrite(&newPoolConf) 172 | } 173 | 174 | if err := newPoolConf.checkMandatoryFields(); err != nil { 175 | return nil, err 176 | } 177 | return &newPoolConf, nil 178 | } 179 | 180 | func WithSSLConfig(sslConfig *tls.Config) SessionPoolConfOption { 181 | return func(conf *SessionPoolConf) { 182 | conf.sslConfig = sslConfig 183 | } 184 | } 185 | 186 | func WithTimeOut(timeOut time.Duration) SessionPoolConfOption { 187 | return func(conf *SessionPoolConf) { 188 | conf.timeOut = timeOut 189 | } 190 | } 191 | 192 | func WithIdleTime(idleTime time.Duration) SessionPoolConfOption { 193 | return func(conf *SessionPoolConf) { 194 | conf.idleTime = idleTime 195 | } 196 | } 197 | 198 | func WithMaxSize(maxSize int) SessionPoolConfOption { 199 | return func(conf *SessionPoolConf) { 200 | conf.maxSize = maxSize 201 | } 202 | } 203 | 204 | func WithMinSize(minSize int) SessionPoolConfOption { 205 | return func(conf *SessionPoolConf) { 206 | conf.minSize = minSize 207 | } 208 | } 209 | 210 | func WithHTTP2(useHTTP2 bool) SessionPoolConfOption { 211 | return func(conf *SessionPoolConf) { 212 | conf.useHTTP2 = useHTTP2 213 | } 214 | } 215 | 216 | func WithHttpHeader(header http.Header) SessionPoolConfOption { 217 | return func(conf *SessionPoolConf) { 218 | conf.httpHeader = header 219 | } 220 | } 221 | 222 | func WithHandshakeKey(handshakeKey string) SessionPoolConfOption { 223 | return func(conf *SessionPoolConf) { 224 | conf.handshakeKey = handshakeKey 225 | } 226 | } 227 | 228 | func (conf *SessionPoolConf) checkMandatoryFields() error { 229 | // Check mandatory fields 230 | if conf.username == "" { 231 | return fmt.Errorf("invalid session pool config: Username is empty") 232 | } 233 | if conf.password == "" { 234 | return fmt.Errorf("invalid session pool config: Password is empty") 235 | } 236 | if len(conf.serviceAddrs) == 0 { 237 | return fmt.Errorf("invalid session pool config: Service address is empty") 238 | } 239 | if conf.spaceName == "" { 240 | return fmt.Errorf("invalid session pool config: Space name is empty") 241 | } 242 | return nil 243 | } 244 | 245 | // checkBasicFields checks the basic fields of the config and 246 | // sets a default value if the given field value is invalid 247 | func (conf *SessionPoolConf) checkBasicFields(log Logger) { 248 | // Check pool related fields, use default value if the given value is invalid 249 | if conf.timeOut < 0 { 250 | conf.timeOut = 0 * time.Millisecond 251 | log.Warn("Illegal Timeout value, the default value of 0 second has been applied") 252 | } 253 | if conf.idleTime < 0 { 254 | conf.idleTime = 0 * time.Millisecond 255 | log.Warn("Invalid IdleTime value, the default value of 0 second has been applied") 256 | } 257 | if conf.maxSize < 1 { 258 | conf.maxSize = 10 259 | log.Warn("Invalid MaxSize value, the default value of 10 has been applied") 260 | } 261 | if conf.minSize < 0 { 262 | conf.minSize = 0 263 | log.Warn("Invalid MinSize value, the default value of 0 has been applied") 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /connection.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2020 vesoft inc. All rights reserved. 4 | * 5 | * This source code is licensed under Apache 2.0 License. 6 | * 7 | */ 8 | 9 | package nebula_go 10 | 11 | import ( 12 | "context" 13 | "crypto/tls" 14 | "fmt" 15 | "net" 16 | "net/http" 17 | "strconv" 18 | "time" 19 | 20 | "github.com/vesoft-inc/fbthrift/thrift/lib/go/thrift" 21 | "github.com/vesoft-inc/nebula-go/v3/nebula" 22 | "github.com/vesoft-inc/nebula-go/v3/nebula/graph" 23 | "golang.org/x/net/http2" 24 | ) 25 | 26 | type connection struct { 27 | severAddress HostAddress 28 | timeout time.Duration 29 | returnedAt time.Time // the connection was created or returned. 30 | sslConfig *tls.Config 31 | useHTTP2 bool 32 | httpHeader http.Header 33 | handshakeKey string 34 | graph *graph.GraphServiceClient 35 | } 36 | 37 | func newConnection(severAddress HostAddress) *connection { 38 | return &connection{ 39 | severAddress: severAddress, 40 | timeout: 0 * time.Millisecond, 41 | returnedAt: time.Now(), 42 | sslConfig: nil, 43 | handshakeKey: "", 44 | graph: nil, 45 | } 46 | } 47 | 48 | // open opens a transport for the connection 49 | // if sslConfig is not nil, an SSL transport will be created 50 | func (cn *connection) open(hostAddress HostAddress, timeout time.Duration, sslConfig *tls.Config, 51 | useHTTP2 bool, httpHeader http.Header, handshakeKey string) error { 52 | ip := hostAddress.Host 53 | port := hostAddress.Port 54 | newAdd := net.JoinHostPort(ip, strconv.Itoa(port)) 55 | cn.timeout = timeout 56 | cn.useHTTP2 = useHTTP2 57 | cn.handshakeKey = handshakeKey 58 | 59 | var ( 60 | err error 61 | transport thrift.Transport 62 | pf thrift.ProtocolFactory 63 | ) 64 | if useHTTP2 { 65 | if sslConfig != nil { 66 | transport, err = thrift.NewHTTPPostClientWithOptions("https://"+newAdd, thrift.HTTPClientOptions{ 67 | Client: &http.Client{ 68 | Transport: &http2.Transport{ 69 | TLSClientConfig: sslConfig, 70 | }, 71 | }, 72 | }) 73 | } else { 74 | transport, err = thrift.NewHTTPPostClientWithOptions("http://"+newAdd, thrift.HTTPClientOptions{ 75 | Client: &http.Client{ 76 | Transport: &http2.Transport{ 77 | // So http2.Transport doesn't complain the URL scheme isn't 'https' 78 | AllowHTTP: true, 79 | // Pretend we are dialing a TLS endpoint. (Note, we ignore the passed tls.Config) 80 | DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) { 81 | _ = cfg 82 | var d net.Dialer 83 | return d.DialContext(ctx, network, addr) 84 | }, 85 | }, 86 | }, 87 | }) 88 | } 89 | if err != nil { 90 | return fmt.Errorf("failed to create a net.Conn-backed Transport,: %s", err.Error()) 91 | } 92 | pf = thrift.NewBinaryProtocolFactoryDefault() 93 | if httpHeader != nil { 94 | client, ok := transport.(*thrift.HTTPClient) 95 | if !ok { 96 | return fmt.Errorf("failed to get thrift http client") 97 | } 98 | for k, vv := range httpHeader { 99 | if k == "Content-Type" { 100 | // fbthrift will add "Content-Type" header, so we need to skip it 101 | continue 102 | } 103 | for _, v := range vv { 104 | // fbthrift set header with http.Header.Add, so we need to set header one by one 105 | client.SetHeader(k, v) 106 | } 107 | } 108 | } 109 | } else { 110 | bufferSize := 128 << 10 111 | 112 | var sock thrift.Transport 113 | if sslConfig != nil { 114 | sock, err = thrift.NewSSLSocketTimeout(newAdd, sslConfig, timeout) 115 | } else { 116 | sock, err = thrift.NewSocket(thrift.SocketAddr(newAdd), thrift.SocketTimeout(timeout)) 117 | } 118 | if err != nil { 119 | return fmt.Errorf("failed to create a net.Conn-backed Transport,: %s", err.Error()) 120 | } 121 | // Set transport 122 | bufferedTranFactory := thrift.NewBufferedTransportFactory(bufferSize) 123 | transport = thrift.NewHeaderTransport(bufferedTranFactory.GetTransport(sock)) 124 | pf = thrift.NewHeaderProtocolFactory() 125 | } 126 | 127 | cn.graph = graph.NewGraphServiceClientFactory(transport, pf) 128 | if err = cn.graph.Open(); err != nil { 129 | return fmt.Errorf("failed to open transport, error: %s", err.Error()) 130 | } 131 | if !cn.graph.IsOpen() { 132 | return fmt.Errorf("transport is off") 133 | } 134 | return cn.verifyClientVersion() 135 | } 136 | 137 | func (cn *connection) verifyClientVersion() error { 138 | req := graph.NewVerifyClientVersionReq() 139 | if cn.handshakeKey != "" { 140 | req.SetVersion([]byte(cn.handshakeKey)) 141 | } 142 | resp, err := cn.graph.VerifyClientVersion(req) 143 | if err != nil { 144 | cn.close() 145 | return fmt.Errorf("failed to verify client handshakeKey: %s", err.Error()) 146 | } 147 | if resp.GetErrorCode() != nebula.ErrorCode_SUCCEEDED { 148 | return fmt.Errorf("incompatible handshakeKey between client and server: %s", string(resp.GetErrorMsg())) 149 | } 150 | return nil 151 | } 152 | 153 | // reopen reopens the current connection. 154 | // Because the code generated by Fbthrift does not handle the seqID, 155 | // the message will be dislocated when the timeout occurs, resulting in unexpected response. 156 | // When the timeout occurs, the connection will be reopened to avoid the impact of the message. 157 | func (cn *connection) reopen() error { 158 | cn.close() 159 | return cn.open(cn.severAddress, cn.timeout, cn.sslConfig, cn.useHTTP2, cn.httpHeader, cn.handshakeKey) 160 | } 161 | 162 | // Authenticate 163 | func (cn *connection) authenticate(username, password string) (*graph.AuthResponse, error) { 164 | resp, err := cn.graph.Authenticate([]byte(username), []byte(password)) 165 | if err != nil { 166 | err = fmt.Errorf("authentication fails, %s", err.Error()) 167 | if e := cn.graph.Close(); e != nil { 168 | err = fmt.Errorf("fail to close transport, error: %s", e.Error()) 169 | } 170 | return nil, err 171 | } 172 | 173 | return resp, nil 174 | } 175 | 176 | func (cn *connection) execute(sessionID int64, stmt string) (*graph.ExecutionResponse, error) { 177 | return cn.executeWithParameter(sessionID, stmt, map[string]*nebula.Value{}) 178 | } 179 | 180 | func (cn *connection) executeWithParameter(sessionID int64, stmt string, 181 | params map[string]*nebula.Value) (*graph.ExecutionResponse, error) { 182 | resp, err := cn.graph.ExecuteWithParameter(sessionID, []byte(stmt), params) 183 | if err != nil { 184 | return nil, err 185 | } 186 | 187 | return resp, nil 188 | } 189 | 190 | func (cn *connection) executeWithParameterTimeout(sessionID int64, stmt string, params map[string]*nebula.Value, timeoutMs int64) (*graph.ExecutionResponse, error) { 191 | return cn.graph.ExecuteWithTimeout(sessionID, []byte(stmt), params, timeoutMs) 192 | } 193 | 194 | func (cn *connection) executeJson(sessionID int64, stmt string) ([]byte, error) { 195 | return cn.ExecuteJsonWithParameter(sessionID, stmt, map[string]*nebula.Value{}) 196 | } 197 | 198 | func (cn *connection) ExecuteJsonWithParameter(sessionID int64, stmt string, params map[string]*nebula.Value) ([]byte, error) { 199 | jsonResp, err := cn.graph.ExecuteJsonWithParameter(sessionID, []byte(stmt), params) 200 | if err != nil { 201 | // reopen the connection if timeout 202 | _, ok := err.(thrift.TransportException) 203 | if ok { 204 | if err.(thrift.TransportException).TypeID() == thrift.TIMED_OUT { 205 | reopenErr := cn.reopen() 206 | if reopenErr != nil { 207 | return nil, reopenErr 208 | } 209 | return cn.graph.ExecuteJsonWithParameter(sessionID, []byte(stmt), params) 210 | } 211 | } 212 | } 213 | 214 | return jsonResp, err 215 | } 216 | 217 | // Check connection to host address 218 | func (cn *connection) ping() bool { 219 | _, err := cn.execute(0, "YIELD 1") 220 | return err == nil 221 | } 222 | 223 | // Sign out and release session ID 224 | func (cn *connection) signOut(sessionID int64) error { 225 | // Release session ID to graphd 226 | return cn.graph.Signout(sessionID) 227 | } 228 | 229 | // Update returnedAt for cleaner 230 | func (cn *connection) release() { 231 | cn.returnedAt = time.Now() 232 | } 233 | 234 | // Close transport 235 | func (cn *connection) close() { 236 | cn.graph.Close() 237 | } 238 | -------------------------------------------------------------------------------- /connection_pool.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2020 vesoft inc. All rights reserved. 4 | * 5 | * This source code is licensed under Apache 2.0 License. 6 | * 7 | */ 8 | 9 | package nebula_go 10 | 11 | import ( 12 | "container/list" 13 | "crypto/tls" 14 | "fmt" 15 | "net/http" 16 | "sync" 17 | "time" 18 | 19 | "github.com/vesoft-inc/nebula-go/v3/nebula" 20 | ) 21 | 22 | type ConnectionPool struct { 23 | idleConnectionQueue list.List 24 | activeConnectionQueue list.List 25 | addresses []HostAddress 26 | conf PoolConfig 27 | hostIndex int 28 | log Logger 29 | rwLock sync.RWMutex 30 | cleanerChan chan struct{} // notify when pool is close 31 | closed bool 32 | sslConfig *tls.Config 33 | } 34 | 35 | // NewConnectionPool constructs a new connection pool using the given addresses and configs 36 | func NewConnectionPool(addresses []HostAddress, conf PoolConfig, log Logger) (*ConnectionPool, error) { 37 | return NewSslConnectionPool(addresses, conf, nil, log) 38 | } 39 | 40 | // NewConnectionPool constructs a new SSL connection pool using the given addresses and configs 41 | func NewSslConnectionPool(addresses []HostAddress, conf PoolConfig, sslConfig *tls.Config, log Logger) (*ConnectionPool, error) { 42 | // Check input 43 | if len(addresses) == 0 { 44 | return nil, fmt.Errorf("failed to initialize connection pool: illegal address input") 45 | } 46 | 47 | // Check config 48 | conf.validateConf(log) 49 | 50 | newPool := &ConnectionPool{ 51 | conf: conf, 52 | log: log, 53 | addresses: addresses, 54 | hostIndex: 0, 55 | sslConfig: sslConfig, 56 | } 57 | 58 | // Init pool with SSL socket 59 | if err := newPool.initPool(); err != nil { 60 | return nil, err 61 | } 62 | newPool.startCleaner() 63 | return newPool, nil 64 | } 65 | 66 | // initPool initializes the connection pool 67 | func (pool *ConnectionPool) initPool() error { 68 | if err := checkAddresses(pool.conf.TimeOut, pool.addresses, pool.sslConfig, 69 | pool.conf.UseHTTP2, pool.conf.HttpHeader, pool.conf.HandshakeKey); err != nil { 70 | return fmt.Errorf("failed to open connection, error: %s ", err.Error()) 71 | } 72 | 73 | for i := 0; i < pool.conf.MinConnPoolSize; i++ { 74 | // Simple round-robin 75 | newConn := newConnection(pool.addresses[i%len(pool.addresses)]) 76 | 77 | // Open connection to host 78 | if err := newConn.open(newConn.severAddress, pool.conf.TimeOut, pool.sslConfig, 79 | pool.conf.UseHTTP2, pool.conf.HttpHeader, pool.conf.HandshakeKey); err != nil { 80 | // If initialization failed, clean idle queue 81 | idleLen := pool.idleConnectionQueue.Len() 82 | for i := 0; i < idleLen; i++ { 83 | pool.idleConnectionQueue.Front().Value.(*connection).close() 84 | pool.idleConnectionQueue.Remove(pool.idleConnectionQueue.Front()) 85 | } 86 | return fmt.Errorf("failed to open connection, error: %s ", err.Error()) 87 | } 88 | // Mark connection as in use 89 | pool.idleConnectionQueue.PushBack(newConn) 90 | } 91 | return nil 92 | } 93 | 94 | // GetSession authenticates the username and password. 95 | // It returns a session if the authentication succeed. 96 | func (pool *ConnectionPool) GetSession(username, password string) (*Session, error) { 97 | // Get valid and usable connection 98 | var conn *connection = nil 99 | var err error = nil 100 | const retryTimes = 3 101 | for i := 0; i < retryTimes; i++ { 102 | conn, err = pool.getIdleConn() 103 | if err == nil { 104 | break 105 | } 106 | } 107 | if conn == nil { 108 | return nil, err 109 | } 110 | // Authenticate 111 | resp, err := conn.authenticate(username, password) 112 | if err != nil { 113 | // if authentication failed, put connection back 114 | pool.pushBack(conn) 115 | return nil, err 116 | } 117 | 118 | // Check auth response 119 | if resp.GetErrorCode() != nebula.ErrorCode_SUCCEEDED { 120 | // if authentication responded with unsuccessful code, put connection back 121 | pool.pushBack(conn) 122 | return nil, fmt.Errorf("failed to authenticate, error code: %d, error msg: %s", 123 | resp.GetErrorCode(), resp.GetErrorMsg()) 124 | } 125 | 126 | sessID := resp.GetSessionID() 127 | timezoneOffset := resp.GetTimeZoneOffsetSeconds() 128 | timezoneName := resp.GetTimeZoneName() 129 | // Create new session 130 | newSession := Session{ 131 | sessionID: sessID, 132 | connection: conn, 133 | connPool: pool, 134 | log: pool.log, 135 | timezoneInfo: timezoneInfo{timezoneOffset, timezoneName}, 136 | } 137 | 138 | return &newSession, nil 139 | } 140 | 141 | func (pool *ConnectionPool) getIdleConn() (*connection, error) { 142 | pool.rwLock.Lock() 143 | defer pool.rwLock.Unlock() 144 | 145 | // Take an idle valid connection if possible 146 | if pool.idleConnectionQueue.Len() > 0 { 147 | var newConn *connection = nil 148 | var newEle *list.Element = nil 149 | var tmpNextEle *list.Element 150 | for ele := pool.idleConnectionQueue.Front(); ele != nil; ele = tmpNextEle { 151 | // Check if connection is valid 152 | if res := ele.Value.(*connection).ping(); res { 153 | newConn = ele.Value.(*connection) 154 | newEle = ele 155 | break 156 | } else { 157 | tmpNextEle = ele.Next() 158 | pool.idleConnectionQueue.Remove(ele) 159 | ele.Value.(*connection).close() 160 | } 161 | } 162 | if newConn == nil { 163 | return pool.createConnection() 164 | } 165 | // Remove new connection from idle and add to active if found 166 | pool.idleConnectionQueue.Remove(newEle) 167 | pool.activeConnectionQueue.PushBack(newConn) 168 | return newConn, nil 169 | } 170 | 171 | // Create a new connection if there is no idle connection and total connection < pool max size 172 | newConn, err := pool.createConnection() 173 | // TODO: If no idle available, wait for timeout and reconnect 174 | return newConn, err 175 | } 176 | 177 | // Release connection to pool 178 | func (pool *ConnectionPool) release(conn *connection) { 179 | pool.deactivate(conn, true, true) 180 | } 181 | 182 | func (pool *ConnectionPool) pushBack(conn *connection) { 183 | pool.deactivate(conn, true, false) 184 | } 185 | 186 | // Deactivate connection. Add to idleConnectionQueue if pushBack is true. 187 | // Release connection if release is true. 188 | func (pool *ConnectionPool) deactivate(conn *connection, pushBack, release bool) { 189 | pool.rwLock.Lock() 190 | defer pool.rwLock.Unlock() 191 | // Remove connection from active queue and add into idle queue 192 | removeFromList(&pool.activeConnectionQueue, conn) 193 | if release { 194 | conn.release() 195 | } 196 | if pushBack { 197 | pool.idleConnectionQueue.PushBack(conn) 198 | } 199 | } 200 | 201 | // Ping checks availability of host 202 | func (pool *ConnectionPool) Ping(host HostAddress, timeout time.Duration) error { 203 | return pingAddress(host, timeout, pool.sslConfig, pool.conf.UseHTTP2, pool.conf.HttpHeader, pool.conf.HandshakeKey) 204 | } 205 | 206 | // Close closes all connection 207 | func (pool *ConnectionPool) Close() { 208 | pool.rwLock.Lock() 209 | defer pool.rwLock.Unlock() 210 | 211 | // TODO(Aiee) merge 2 lists and close all connections 212 | idleLen := pool.idleConnectionQueue.Len() 213 | activeLen := pool.activeConnectionQueue.Len() 214 | 215 | for i := 0; i < idleLen; i++ { 216 | pool.idleConnectionQueue.Front().Value.(*connection).close() 217 | pool.idleConnectionQueue.Remove(pool.idleConnectionQueue.Front()) 218 | } 219 | for i := 0; i < activeLen; i++ { 220 | pool.activeConnectionQueue.Front().Value.(*connection).close() 221 | pool.activeConnectionQueue.Remove(pool.activeConnectionQueue.Front()) 222 | } 223 | 224 | pool.closed = true 225 | if pool.cleanerChan != nil { 226 | close(pool.cleanerChan) 227 | } 228 | } 229 | 230 | func (pool *ConnectionPool) getActiveConnCount() int { 231 | return pool.activeConnectionQueue.Len() 232 | } 233 | 234 | func (pool *ConnectionPool) getIdleConnCount() int { 235 | return pool.idleConnectionQueue.Len() 236 | } 237 | 238 | // Get a valid host (round robin) 239 | func (pool *ConnectionPool) getHost() HostAddress { 240 | if pool.hostIndex == len(pool.addresses) { 241 | pool.hostIndex = 0 242 | } 243 | host := pool.addresses[pool.hostIndex] 244 | pool.hostIndex++ 245 | return host 246 | } 247 | 248 | // Select a new host to create a new connection 249 | func (pool *ConnectionPool) newConnToHost() (*connection, error) { 250 | // Get a valid host (round robin) 251 | host := pool.getHost() 252 | newConn := newConnection(host) 253 | // Open connection to host 254 | if err := newConn.open(newConn.severAddress, pool.conf.TimeOut, pool.sslConfig, 255 | pool.conf.UseHTTP2, pool.conf.HttpHeader, pool.conf.HandshakeKey); err != nil { 256 | return nil, err 257 | } 258 | // Add connection to active queue 259 | pool.activeConnectionQueue.PushBack(newConn) 260 | // TODO: update workload 261 | return newConn, nil 262 | } 263 | 264 | // Remove a connection from list 265 | func removeFromList(l *list.List, conn *connection) { 266 | for ele := l.Front(); ele != nil; ele = ele.Next() { 267 | if ele.Value.(*connection) == conn { 268 | l.Remove(ele) 269 | } 270 | } 271 | } 272 | 273 | // Compare total connection number with pool max size and return a connection if capable 274 | func (pool *ConnectionPool) createConnection() (*connection, error) { 275 | totalConn := pool.idleConnectionQueue.Len() + pool.activeConnectionQueue.Len() 276 | // If no idle available and the number of total connection reaches the max pool size, return error/wait for timeout 277 | if totalConn >= pool.conf.MaxConnPoolSize { 278 | return nil, fmt.Errorf("failed to get connection: No valid connection" + 279 | " in the idle queue and connection number has reached the pool capacity") 280 | } 281 | 282 | newConn, err := pool.newConnToHost() 283 | if err != nil { 284 | return nil, err 285 | } 286 | // TODO: update workload 287 | return newConn, nil 288 | } 289 | 290 | // startCleaner starts connectionCleaner if idleTime > 0. 291 | func (pool *ConnectionPool) startCleaner() { 292 | if pool.conf.IdleTime > 0 && pool.cleanerChan == nil { 293 | pool.cleanerChan = make(chan struct{}, 1) 294 | go pool.connectionCleaner() 295 | } 296 | } 297 | 298 | func (pool *ConnectionPool) connectionCleaner() { 299 | const minInterval = time.Minute 300 | 301 | d := pool.conf.IdleTime 302 | 303 | if d < minInterval { 304 | d = minInterval 305 | } 306 | t := time.NewTimer(d) 307 | 308 | for { 309 | select { 310 | case <-t.C: 311 | case <-pool.cleanerChan: // pool was closed. 312 | } 313 | 314 | pool.rwLock.Lock() 315 | 316 | if pool.closed { 317 | pool.cleanerChan = nil 318 | pool.rwLock.Unlock() 319 | return 320 | } 321 | 322 | closing := pool.timeoutConnectionList() 323 | pool.rwLock.Unlock() 324 | for _, c := range closing { 325 | c.close() 326 | } 327 | 328 | t.Reset(d) 329 | } 330 | } 331 | 332 | func (pool *ConnectionPool) timeoutConnectionList() (closing []*connection) { 333 | if pool.conf.IdleTime > 0 { 334 | expiredSince := time.Now().Add(-pool.conf.IdleTime) 335 | var newEle *list.Element 336 | 337 | maxCleanSize := pool.idleConnectionQueue.Len() + pool.activeConnectionQueue.Len() - pool.conf.MinConnPoolSize 338 | 339 | for ele := pool.idleConnectionQueue.Front(); ele != nil; { 340 | if maxCleanSize == 0 { 341 | return 342 | } 343 | 344 | newEle = ele.Next() 345 | // Check connection is expired 346 | if !ele.Value.(*connection).returnedAt.Before(expiredSince) { 347 | return 348 | } 349 | closing = append(closing, ele.Value.(*connection)) 350 | pool.idleConnectionQueue.Remove(ele) 351 | ele = newEle 352 | maxCleanSize-- 353 | } 354 | } 355 | return 356 | } 357 | 358 | // checkAddresses checks addresses availability 359 | // It opens a temporary connection to each address and closes it immediately. 360 | // If no error is returned, the addresses are available. 361 | func checkAddresses( 362 | confTimeout time.Duration, addresses []HostAddress, sslConfig *tls.Config, 363 | useHTTP2 bool, httpHeader http.Header, handshakeKey string, 364 | ) error { 365 | timeout := 3 * time.Second 366 | if confTimeout != 0 && confTimeout < timeout { 367 | timeout = confTimeout 368 | } 369 | for _, address := range addresses { 370 | if err := pingAddress(address, timeout, sslConfig, useHTTP2, httpHeader, handshakeKey); err != nil { 371 | return err 372 | } 373 | } 374 | return nil 375 | } 376 | 377 | func pingAddress( 378 | address HostAddress, timeout time.Duration, sslConfig *tls.Config, 379 | useHTTP2 bool, httpHeader http.Header, handshakeKey string, 380 | ) error { 381 | newConn := newConnection(address) 382 | // Open connection to host 383 | if err := newConn.open(newConn.severAddress, timeout, sslConfig, useHTTP2, httpHeader, handshakeKey); err != nil { 384 | return err 385 | } 386 | defer newConn.close() 387 | return nil 388 | } 389 | -------------------------------------------------------------------------------- /const.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2024 vesoft inc. All rights reserved. 4 | * 5 | * This source code is licensed under Apache 2.0 License. 6 | * 7 | */ 8 | 9 | package nebula_go 10 | 11 | const ( 12 | ErrorTagNotFound = "TagNotFound: Tag not existed!" 13 | ErrorEdgeNotFound = "EdgeNotFound: Edge not existed!" 14 | ) 15 | -------------------------------------------------------------------------------- /examples/basic_example/graph_client_basic_example.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2021 vesoft inc. All rights reserved. 4 | * 5 | * This source code is licensed under Apache 2.0 License. 6 | * 7 | */ 8 | 9 | package main 10 | 11 | import ( 12 | "fmt" 13 | 14 | nebula "github.com/vesoft-inc/nebula-go/v3" 15 | ) 16 | 17 | const ( 18 | address = "127.0.0.1" 19 | // The default port of NebulaGraph 2.x is 9669. 20 | // 3699 is only for testing. 21 | port = 3699 22 | username = "root" 23 | password = "nebula" 24 | useHTTP2 = false 25 | ) 26 | 27 | // Initialize logger 28 | var log = nebula.DefaultLogger{} 29 | 30 | func main() { 31 | hostAddress := nebula.HostAddress{Host: address, Port: port} 32 | hostList := []nebula.HostAddress{hostAddress} 33 | // Create configs for connection pool using default values 34 | testPoolConfig := nebula.GetDefaultConf() 35 | testPoolConfig.UseHTTP2 = useHTTP2 36 | testPoolConfig.HandshakeKey = "3.0.0" 37 | 38 | // Initialize connection pool 39 | pool, err := nebula.NewConnectionPool(hostList, testPoolConfig, log) 40 | if err != nil { 41 | log.Fatal(fmt.Sprintf("Fail to initialize the connection pool, host: %s, port: %d, %s", address, port, err.Error())) 42 | } 43 | // Close all connections in the pool 44 | defer pool.Close() 45 | 46 | // Create session 47 | session, err := pool.GetSession(username, password) 48 | if err != nil { 49 | log.Fatal(fmt.Sprintf("Fail to create a new session from connection pool, username: %s, password: %s, %s", 50 | username, password, err.Error())) 51 | } 52 | // Release session and return connection back to connection pool 53 | defer session.Release() 54 | 55 | checkResultSet := func(prefix string, res *nebula.ResultSet) { 56 | if !res.IsSucceed() { 57 | log.Fatal(fmt.Sprintf("%s, ErrorCode: %v, ErrorMsg: %s", prefix, res.GetErrorCode(), res.GetErrorMsg())) 58 | } 59 | } 60 | 61 | { 62 | // Prepare the query 63 | createSchema := "CREATE SPACE IF NOT EXISTS basic_example_space(vid_type=FIXED_STRING(20)); " + 64 | "USE basic_example_space;" + 65 | "CREATE TAG IF NOT EXISTS person(name string, age int);" + 66 | "CREATE EDGE IF NOT EXISTS like(likeness double)" 67 | 68 | // Execute a query 69 | resultSet, err := session.Execute(createSchema) 70 | if err != nil { 71 | fmt.Print(err.Error()) 72 | return 73 | } 74 | checkResultSet(createSchema, resultSet) 75 | } 76 | // Drop space 77 | { 78 | query := "DROP SPACE IF EXISTS basic_example_space" 79 | // Send query 80 | resultSet, err := session.Execute(query) 81 | if err != nil { 82 | fmt.Print(err.Error()) 83 | return 84 | } 85 | checkResultSet(query, resultSet) 86 | } 87 | 88 | fmt.Print("\n") 89 | log.Info("Nebula Go Client Basic Example Finished") 90 | } 91 | -------------------------------------------------------------------------------- /examples/goroutines_example/graph_client_goroutines_example.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2021 vesoft inc. All rights reserved. 4 | * 5 | * This source code is licensed under Apache 2.0 License. 6 | * 7 | */ 8 | 9 | package main 10 | 11 | import ( 12 | "fmt" 13 | "strings" 14 | "sync" 15 | "time" 16 | 17 | nebula "github.com/vesoft-inc/nebula-go/v3" 18 | ) 19 | 20 | const ( 21 | address = "127.0.0.1" 22 | // The default port of NebulaGraph 2.x is 9669. 23 | // 3699 is only for testing. 24 | port = 3699 25 | username = "root" 26 | password = "nebula" 27 | useHTTP2 = false 28 | ) 29 | 30 | // Initialize logger 31 | var log = nebula.DefaultLogger{} 32 | 33 | func main() { 34 | hostAddress := nebula.HostAddress{Host: address, Port: port} 35 | hostList := []nebula.HostAddress{hostAddress} 36 | // Create configs for connection pool using default values 37 | testPoolConfig := nebula.GetDefaultConf() 38 | testPoolConfig.UseHTTP2 = useHTTP2 39 | 40 | // Initialize connection pool 41 | pool, err := nebula.NewConnectionPool(hostList, testPoolConfig, log) 42 | if err != nil { 43 | log.Fatal(fmt.Sprintf("Fail to initialize the connection pool, host: %s, port: %d, %s", address, port, err.Error())) 44 | } 45 | // Close all connections in the pool 46 | defer pool.Close() 47 | // Create session and send query in go routine 48 | var wg sync.WaitGroup 49 | wg.Add(1) 50 | go func(wg *sync.WaitGroup) { 51 | defer wg.Done() 52 | // Create session 53 | session, err := pool.GetSession(username, password) 54 | if err != nil { 55 | log.Fatal(fmt.Sprintf("Fail to create a new session from connection pool, username: %s, password: %s, %s", 56 | username, password, err.Error())) 57 | } 58 | // Release session and return connection back to connection pool 59 | defer session.Release() 60 | // Method used to check execution response 61 | checkResultSet := func(prefix string, res *nebula.ResultSet) { 62 | if !res.IsSucceed() { 63 | log.Fatal(fmt.Sprintf("%s, ErrorCode: %v, ErrorMsg: %s", prefix, res.GetErrorCode(), res.GetErrorMsg())) 64 | } 65 | } 66 | { 67 | createSchema := "CREATE SPACE IF NOT EXISTS example_space(vid_type=FIXED_STRING(20)); " + 68 | "USE example_space;" + 69 | "CREATE TAG IF NOT EXISTS person(name string, age int);" + 70 | "CREATE EDGE IF NOT EXISTS like(likeness double)" 71 | 72 | // Execute a query 73 | resultSet, err := session.Execute(createSchema) 74 | if err != nil { 75 | fmt.Print(err.Error()) 76 | return 77 | } 78 | checkResultSet(createSchema, resultSet) 79 | } 80 | time.Sleep(5 * time.Second) 81 | { 82 | insertVertexes := "INSERT VERTEX person(name, age) VALUES " + 83 | "'Bob':('Bob', 10), " + 84 | "'Lily':('Lily', 9), " + 85 | "'Tom':('Tom', 10), " + 86 | "'Jerry':('Jerry', 13), " + 87 | "'John':('John', 11);" 88 | 89 | // Insert multiple vertexes 90 | resultSet, err := session.Execute(insertVertexes) 91 | if err != nil { 92 | fmt.Print(err.Error()) 93 | return 94 | } 95 | checkResultSet(insertVertexes, resultSet) 96 | } 97 | { 98 | // Insert multiple edges 99 | insertEdges := "INSERT EDGE like(likeness) VALUES " + 100 | "'Bob'->'Lily':(80.0), " + 101 | "'Bob'->'Tom':(70.0), " + 102 | "'Lily'->'Jerry':(84.0), " + 103 | "'Tom'->'Jerry':(68.3), " + 104 | "'Bob'->'John':(97.2);" 105 | 106 | resultSet, err := session.Execute(insertEdges) 107 | if err != nil { 108 | fmt.Print(err.Error()) 109 | return 110 | } 111 | checkResultSet(insertEdges, resultSet) 112 | } 113 | // Extract data from the resultSet 114 | { 115 | query := "GO FROM 'Bob' OVER like YIELD $^.person.name, $^.person.age, like.likeness" 116 | // Send query 117 | resultSet, err := session.Execute(query) 118 | if err != nil { 119 | fmt.Print(err.Error()) 120 | return 121 | } 122 | checkResultSet(query, resultSet) 123 | 124 | // Get all column names from the resultSet 125 | colNames := resultSet.GetColNames() 126 | fmt.Printf("column names: %s\n", strings.Join(colNames, ", ")) 127 | 128 | // Get a row from resultSet 129 | record, err := resultSet.GetRowValuesByIndex(0) 130 | if err != nil { 131 | log.Error(err.Error()) 132 | } 133 | // Print whole row 134 | fmt.Printf("row elements: %s\n", record.String()) 135 | // Get a value in the row by column index 136 | valueWrapper, err := record.GetValueByIndex(0) 137 | if err != nil { 138 | log.Error(err.Error()) 139 | } 140 | // Get type of the value 141 | fmt.Printf("valueWrapper type: %s \n", valueWrapper.GetType()) 142 | // Check if valueWrapper is a string type 143 | if valueWrapper.IsString() { 144 | // Convert valueWrapper to a string value 145 | v1Str, err := valueWrapper.AsString() 146 | if err != nil { 147 | log.Error(err.Error()) 148 | } 149 | fmt.Printf("Result of ValueWrapper.AsString(): %s\n", v1Str) 150 | } 151 | // Print ValueWrapper using String() 152 | fmt.Printf("Print using ValueWrapper.String(): %s", valueWrapper.String()) 153 | } 154 | // Drop space 155 | { 156 | query := "DROP SPACE IF EXISTS example_space" 157 | // Send query 158 | resultSet, err := session.Execute(query) 159 | if err != nil { 160 | fmt.Print(err.Error()) 161 | return 162 | } 163 | checkResultSet(query, resultSet) 164 | } 165 | }(&wg) 166 | wg.Wait() 167 | 168 | fmt.Print("\n") 169 | log.Info("Nebula Go Client Goroutines Example Finished") 170 | } 171 | -------------------------------------------------------------------------------- /examples/json_example/parse_json_example.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2021 vesoft inc. All rights reserved. 4 | * 5 | * This source code is licensed under Apache 2.0 License. 6 | * 7 | */ 8 | 9 | package main 10 | 11 | import ( 12 | "encoding/json" 13 | "fmt" 14 | "time" 15 | 16 | nebula "github.com/vesoft-inc/nebula-go/v3" 17 | ) 18 | 19 | const ( 20 | address = "127.0.0.1" 21 | // The default port of NebulaGraph 2.x is 9669. 22 | // 3699 is only for testing. 23 | port = 3699 24 | username = "root" 25 | password = "nebula" 26 | useHTTP2 = false 27 | ) 28 | 29 | // Initialize logger 30 | var log = nebula.DefaultLogger{} 31 | 32 | // Struct used for storing the parsed object 33 | type JsonObj struct { 34 | Results []struct { 35 | Columns []string `json:"columns"` 36 | Data []struct { 37 | Row []interface{} `json:"row"` 38 | Meta []interface{} `json:"meta"` 39 | } `json:"data"` 40 | LatencyInUs int `json:"latencyInUs"` 41 | SpaceName string `json:"spaceName"` 42 | PlanDesc struct { 43 | PlanNodeDescs []struct { 44 | Name string `json:"name"` 45 | ID int `json:"id"` 46 | OutputVar string `json:"outputVar"` 47 | Description struct { 48 | Key string `json:"key"` 49 | } `json:"description"` 50 | Profiles []struct { 51 | Rows int `json:"rows"` 52 | ExecDurationInUs int `json:"execDurationInUs"` 53 | TotalDurationInUs int `json:"totalDurationInUs"` 54 | OtherStats struct { 55 | } `json:"otherStats"` 56 | } `json:"profiles"` 57 | BranchInfo struct { 58 | IsDoBranch bool `json:"isDoBranch"` 59 | ConditionNodeID int `json:"conditionNodeId"` 60 | } `json:"branchInfo"` 61 | Dependencies []interface{} `json:"dependencies"` 62 | } `json:"planNodeDescs"` 63 | NodeIndexMap struct { 64 | } `json:"nodeIndexMap"` 65 | Format string `json:"format"` 66 | OptimizeTimeInUs int `json:"optimize_time_in_us"` 67 | } `json:"planDesc "` 68 | Comment string `json:"comment "` 69 | } `json:"results"` 70 | Errors []struct { 71 | Code int `json:"code"` 72 | Message string `json:"message"` 73 | } `json:"errors"` 74 | } 75 | 76 | func main() { 77 | hostAddress := nebula.HostAddress{Host: address, Port: port} 78 | hostList := []nebula.HostAddress{hostAddress} 79 | // Create configs for connection pool using default values 80 | testPoolConfig := nebula.GetDefaultConf() 81 | testPoolConfig.UseHTTP2 = useHTTP2 82 | 83 | // Initialize connection pool 84 | pool, err := nebula.NewConnectionPool(hostList, testPoolConfig, log) 85 | if err != nil { 86 | log.Fatal(fmt.Sprintf("Fail to initialize the connection pool, host: %s, port: %d, %s", address, port, err.Error())) 87 | } 88 | // Close all connections in the pool 89 | defer pool.Close() 90 | 91 | // Create session 92 | session, err := pool.GetSession(username, password) 93 | if err != nil { 94 | log.Fatal(fmt.Sprintf("Fail to create a new session from connection pool, username: %s, password: %s, %s", 95 | username, password, err.Error())) 96 | } 97 | // Release session and return connection back to connection pool 98 | defer session.Release() 99 | 100 | // Create schemas 101 | createTestDataSchema(session) 102 | // Load data 103 | loadTestData(session) 104 | 105 | // Complex result 106 | { 107 | jsonStrResult, err := session.ExecuteJson("MATCH (v:person {name: \"Bob\"}) RETURN v") 108 | if err != nil { 109 | log.Fatal(fmt.Sprintf("fail to get the result in json format, %s", err.Error())) 110 | } 111 | 112 | var jsonObj JsonObj 113 | // Parse JSON 114 | json.Unmarshal(jsonStrResult, &jsonObj) 115 | // Get row 116 | rowData := jsonObj.Results[0].Data[0].Row[0] 117 | row, err := json.MarshalIndent(rowData, "", " ") 118 | if err != nil { 119 | fmt.Println("error:", err) 120 | } 121 | fmt.Println(string(row)) 122 | 123 | // Get a property 124 | birthday := jsonObj.Results[0].Data[0].Row[0].(map[string]interface{})["person.birthday"] 125 | fmt.Printf("person.birthday is %s \n\n", birthday) 126 | } 127 | // With error 128 | { 129 | jsonStrResult, err := session.ExecuteJson("MATCH (v:person {name: \"Bob\"}) RETURN v") 130 | if err != nil { 131 | log.Fatal(fmt.Sprintf("fail to get the result in json format, %s", err.Error())) 132 | } 133 | 134 | var jsonObj JsonObj 135 | // Parse JSON 136 | json.Unmarshal(jsonStrResult, &jsonObj) 137 | // Get error message 138 | errorMsg := jsonObj.Errors[0].Message 139 | msg, err := json.MarshalIndent(errorMsg, "", " ") 140 | if err != nil { 141 | fmt.Println("error:", err) 142 | } 143 | fmt.Println("Error message: ", string(msg)) 144 | } 145 | dropSpace(session, "client_test") 146 | 147 | fmt.Print("\n") 148 | log.Info("Nebula Go JSON parsing Example Finished") 149 | } 150 | 151 | // creates schema 152 | func createTestDataSchema(session *nebula.Session) { 153 | createSchema := "CREATE SPACE IF NOT EXISTS test_data(vid_type = FIXED_STRING(30));" + 154 | "USE test_data; " + 155 | "CREATE TAG IF NOT EXISTS person(name string, age int8, grade int16, " + 156 | "friends int32, book_num int64, birthday datetime, " + 157 | "start_school date, morning time, property double, " + 158 | "is_girl bool, child_name fixed_string(10), expend float, " + 159 | "first_out_city timestamp, hobby string); " + 160 | "CREATE TAG IF NOT EXISTS student(name string); " + 161 | "CREATE EDGE IF NOT EXISTS like(likeness double); " + 162 | "CREATE EDGE IF NOT EXISTS friend(start_Datetime datetime, end_Datetime datetime); " + 163 | "CREATE TAG INDEX IF NOT EXISTS person_name_index ON person(name(8));" 164 | resultSet, err := session.Execute(createSchema) 165 | if err != nil { 166 | log.Fatal(err.Error()) 167 | return 168 | } 169 | checkResultSet(createSchema, resultSet) 170 | 171 | time.Sleep(3 * time.Second) 172 | } 173 | 174 | // inserts data that used in tests 175 | func loadTestData(session *nebula.Session) { 176 | query := "INSERT VERTEX person(name, age, grade, friends, book_num," + 177 | "birthday, start_school, morning, property," + 178 | "is_girl, child_name, expend, first_out_city) VALUES" + 179 | "'Bob':('Bob', 10, 3, 10, 100, datetime('2010-09-10T10:08:02')," + 180 | "date('2017-09-10'), time('07:10:00'), " + 181 | "1000.0, false, \"Hello World!\", 100.0, 1111)," + 182 | "'Lily':('Lily', 9, 3, 10, 100, datetime('2010-09-10T10:08:02'), " + 183 | "date('2017-09-10'), time('07:10:00'), " + 184 | "1000.0, false, \"Hello World!\", 100.0, 1111)," + 185 | "'Tom':('Tom', 10, 3, 10, 100, datetime('2010-09-10T10:08:02'), " + 186 | "date('2017-09-10'), time('07:10:00'), " + 187 | "1000.0, false, \"Hello World!\", 100.0, 1111)," + 188 | "'Jerry':('Jerry', 9, 3, 10, 100, datetime('2010-09-10T10:08:02')," + 189 | "date('2017-09-10'), time('07:10:00'), " + 190 | "1000.0, false, \"Hello World!\", 100.0, 1111), " + 191 | "'John':('John', 10, 3, 10, 100, datetime('2010-09-10T10:08:02'), " + 192 | "date('2017-09-10'), time('07:10:00'), " + 193 | "1000.0, false, \"Hello World!\", 100.0, 1111)" 194 | resultSet, err := session.Execute(query) 195 | if err != nil { 196 | log.Fatal(err.Error()) 197 | return 198 | } 199 | checkResultSet(query, resultSet) 200 | 201 | query = 202 | "INSERT VERTEX student(name) VALUES " + 203 | "'Bob':('Bob'), 'Lily':('Lily'), " + 204 | "'Tom':('Tom'), 'Jerry':('Jerry'), 'John':('John')" 205 | resultSet, err = session.Execute(query) 206 | if err != nil { 207 | log.Fatal(err.Error()) 208 | return 209 | } 210 | checkResultSet(query, resultSet) 211 | 212 | query = 213 | "INSERT EDGE like(likeness) VALUES " + 214 | "'Bob'->'Lily':(80.0), " + 215 | "'Bob'->'Tom':(70.0), " + 216 | "'Jerry'->'Lily':(84.0)," + 217 | "'Tom'->'Jerry':(68.3), " + 218 | "'Bob'->'John':(97.2)" 219 | resultSet, err = session.Execute(query) 220 | if err != nil { 221 | log.Fatal(err.Error()) 222 | return 223 | } 224 | checkResultSet(query, resultSet) 225 | 226 | query = 227 | "INSERT EDGE friend(start_Datetime, end_Datetime) VALUES " + 228 | "'Bob'->'Lily':(datetime('2008-09-10T10:08:02'), datetime('2010-09-10T10:08:02')), " + 229 | "'Bob'->'Tom':(datetime('2008-09-10T10:08:02'), datetime('2010-09-10T10:08:02')), " + 230 | "'Jerry'->'Lily':(datetime('2008-09-10T10:08:02'), datetime('2010-09-10T10:08:02')), " + 231 | "'Tom'->'Jerry':(datetime('2008-09-10T10:08:02'), datetime('2010-09-10T10:08:02')), " + 232 | "'Bob'->'John':(datetime('2008-09-10T10:08:02'), datetime('2010-09-10T10:08:02'))" 233 | resultSet, err = session.Execute(query) 234 | if err != nil { 235 | log.Fatal(err.Error()) 236 | return 237 | } 238 | checkResultSet(query, resultSet) 239 | } 240 | 241 | func dropSpace(session *nebula.Session, spaceName string) { 242 | query := fmt.Sprintf("DROP SPACE IF EXISTS %s;", spaceName) 243 | resultSet, err := session.Execute(query) 244 | if err != nil { 245 | log.Fatal(err.Error()) 246 | return 247 | } 248 | checkResultSet(query, resultSet) 249 | } 250 | 251 | func checkResultSet(prefix string, res *nebula.ResultSet) { 252 | if !res.IsSucceed() { 253 | log.Fatal(fmt.Sprintf("%s, ErrorCode: %v, ErrorMsg: %s", prefix, res.GetErrorCode(), res.GetErrorMsg())) 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /examples/parameter_example/parameter_example.go: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2021 vesoft inc. All rights reserved. 2 | * 3 | * This source code is licensed under Apache 2.0 License, 4 | * attached with Common Clause Condition 1.0, found in the LICENSES directory. 5 | */ 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "strings" 12 | "sync" 13 | 14 | nebulago "github.com/vesoft-inc/nebula-go/v3" 15 | ) 16 | 17 | const ( 18 | address = "127.0.0.1" 19 | // The default port of NebulaGraph 2.x is 9669. 20 | // 3699 is only for testing. 21 | port = 3699 22 | username = "root" 23 | password = "nebula" 24 | useHTTP2 = false 25 | ) 26 | 27 | // Initialize logger 28 | var log = nebulago.DefaultLogger{} 29 | 30 | func main() { 31 | hostAddress := nebulago.HostAddress{Host: address, Port: port} 32 | hostList := []nebulago.HostAddress{hostAddress} 33 | // Create configs for connection pool using default values 34 | testPoolConfig := nebulago.GetDefaultConf() 35 | testPoolConfig.UseHTTP2 = useHTTP2 36 | 37 | // Initialize connection pool 38 | pool, err := nebulago.NewConnectionPool(hostList, testPoolConfig, log) 39 | if err != nil { 40 | log.Fatal(fmt.Sprintf("Fail to initialize the connection pool, host: %s, port: %d, %s", address, port, err.Error())) 41 | } 42 | // Close all connections in the pool 43 | defer pool.Close() 44 | // Create session and send query in go routine 45 | var wg sync.WaitGroup 46 | wg.Add(1) 47 | go func(wg *sync.WaitGroup) { 48 | defer wg.Done() 49 | // Create session 50 | session, err := pool.GetSession(username, password) 51 | if err != nil { 52 | log.Fatal(fmt.Sprintf("Fail to create a new session from connection pool, username: %s, password: %s, %s", 53 | username, password, err.Error())) 54 | } 55 | // Release session and return connection back to connection pool 56 | defer session.Release() 57 | 58 | checkResultSet := func(prefix string, res *nebulago.ResultSet) { 59 | if !res.IsSucceed() { 60 | log.Fatal(fmt.Sprintf("%s, ErrorCode: %v, ErrorMsg: %s", prefix, res.GetErrorCode(), res.GetErrorMsg())) 61 | } 62 | } 63 | 64 | params := make(map[string]interface{}) 65 | params["p1"] = true 66 | params["p2"] = 3 67 | params["p3"] = []interface{}{true, 3} 68 | params["p4"] = map[string]interface{}{"a": true, "b": 3} 69 | params["p5"] = int64(9223372036854775807) 70 | 71 | // Extract data from the resultSet 72 | { 73 | query := "RETURN abs($p2)+1 AS col1, toBoolean($p1) and false AS col2, $p3, $p4.a, $p5 AS col5" 74 | // Send query 75 | // resultSet, err := session.ExecuteWithParameter(query, params) 76 | resultSet, err := session.ExecuteWithParameter(query, params) 77 | if err != nil { 78 | fmt.Print(err.Error()) 79 | return 80 | } 81 | checkResultSet(query, resultSet) 82 | 83 | // Get all column names from the resultSet 84 | colNames := resultSet.GetColNames() 85 | fmt.Printf("Column names: %s\n", strings.Join(colNames, ", ")) 86 | fmt.Print(resultSet.AsStringTable()) 87 | // Get a row from resultSet 88 | record, err := resultSet.GetRowValuesByIndex(0) 89 | if err != nil { 90 | log.Error(err.Error()) 91 | } 92 | // Print whole row 93 | fmt.Printf("The first row elements: %s\n", record.String()) 94 | 95 | // Specifically check the int64 value 96 | valWrap, err := record.GetValueByIndex(4) // Column 'col5' 97 | if err != nil { 98 | log.Error(err.Error()) 99 | } else { 100 | int64Val, err := valWrap.AsInt() 101 | if err != nil { 102 | log.Error(err.Error()) 103 | } else { 104 | fmt.Printf("The int64 value (col5): %d\n", int64Val) 105 | } 106 | } 107 | } 108 | }(&wg) 109 | wg.Wait() 110 | 111 | fmt.Print("\n") 112 | log.Info("Nebula Go Parameter Example Finished") 113 | } 114 | -------------------------------------------------------------------------------- /examples/session_pool_example/session_pool_example.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2022 vesoft inc. All rights reserved. 4 | * 5 | * This source code is licensed under Apache 2.0 License. 6 | * 7 | */ 8 | 9 | package main 10 | 11 | import ( 12 | "fmt" 13 | "strings" 14 | "sync" 15 | "time" 16 | 17 | nebula "github.com/vesoft-inc/nebula-go/v3" 18 | ) 19 | 20 | const ( 21 | address = "127.0.0.1" 22 | // The default port of NebulaGraph 2.x is 9669. 23 | // 3699 is only for testing. 24 | port = 3699 25 | username = "root" 26 | password = "nebula" 27 | useHTTP2 = false 28 | ) 29 | 30 | // Initialize logger 31 | var log = nebula.DefaultLogger{} 32 | 33 | type Person struct { 34 | Name string `nebula:"name"` 35 | Age int `nebula:"age"` 36 | Likeness float64 `nebula:"likeness"` 37 | } 38 | 39 | func main() { 40 | prepareSpace() 41 | hostAddress := nebula.HostAddress{Host: address, Port: port} 42 | 43 | // Create configs for session pool 44 | config, err := nebula.NewSessionPoolConf( 45 | "root", 46 | "nebula", 47 | []nebula.HostAddress{hostAddress}, 48 | "example_space", 49 | nebula.WithHTTP2(useHTTP2), 50 | ) 51 | if err != nil { 52 | log.Fatal(fmt.Sprintf("failed to create session pool config, %s", err.Error())) 53 | } 54 | 55 | // create session pool 56 | sessionPool, err := nebula.NewSessionPool(*config, nebula.DefaultLogger{}) 57 | if err != nil { 58 | log.Fatal(fmt.Sprintf("failed to initialize session pool, %s", err.Error())) 59 | } 60 | defer sessionPool.Close() 61 | 62 | checkResultSet := func(prefix string, res *nebula.ResultSet) { 63 | if !res.IsSucceed() { 64 | log.Fatal(fmt.Sprintf("%s, ErrorCode: %v, ErrorMsg: %s", prefix, res.GetErrorCode(), res.GetErrorMsg())) 65 | } 66 | } 67 | 68 | // execute query 69 | { 70 | insertVertexes := "INSERT VERTEX person(name, age) VALUES " + 71 | "'Bob':('Bob', 10), " + 72 | "'Lily':('Lily', 9), " + 73 | "'Tom':('Tom', 10), " + 74 | "'Jerry':('Jerry', 13), " + 75 | "'John':('John', 11);" 76 | 77 | // Insert multiple vertexes 78 | resultSet, err := sessionPool.Execute(insertVertexes) 79 | if err != nil { 80 | fmt.Print(err.Error()) 81 | return 82 | } 83 | checkResultSet(insertVertexes, resultSet) 84 | } 85 | { 86 | // Insert multiple edges 87 | insertEdges := "INSERT EDGE like(likeness) VALUES " + 88 | "'Bob'->'Lily':(80.0), " + 89 | "'Bob'->'Tom':(70.0), " + 90 | "'Lily'->'Jerry':(84.0), " + 91 | "'Tom'->'Jerry':(68.3), " + 92 | "'Bob'->'John':(97.2);" 93 | 94 | resultSet, err := sessionPool.Execute(insertEdges) 95 | if err != nil { 96 | fmt.Print(err.Error()) 97 | return 98 | } 99 | checkResultSet(insertEdges, resultSet) 100 | } 101 | // Extract data from the resultSet 102 | { 103 | query := "GO FROM 'Bob' OVER like YIELD $^.person.name AS name, $^.person.age AS age, like.likeness AS likeness" 104 | // Send query in goroutine 105 | wg := sync.WaitGroup{} 106 | wg.Add(1) 107 | var resultSet *nebula.ResultSet 108 | go func(wg *sync.WaitGroup) { 109 | defer wg.Done() 110 | 111 | resultSet, err = sessionPool.Execute(query) 112 | if err != nil { 113 | fmt.Print(err.Error()) 114 | return 115 | } 116 | checkResultSet(query, resultSet) 117 | var personList []Person 118 | resultSet.Scan(&personList) 119 | fmt.Printf("personList: %v\n", personList) 120 | // personList: [{Bob 10 97.2} {Bob 10 80} {Bob 10 70}] 121 | }(&wg) 122 | wg.Wait() 123 | 124 | // Get all column names from the resultSet 125 | colNames := resultSet.GetColNames() 126 | fmt.Printf("column names: %s\n", strings.Join(colNames, ", ")) 127 | 128 | // Get a row from resultSet 129 | record, err := resultSet.GetRowValuesByIndex(0) 130 | if err != nil { 131 | log.Error(err.Error()) 132 | } 133 | // Print whole row 134 | fmt.Printf("row elements: %s\n", record.String()) 135 | // Get a value in the row by column index 136 | valueWrapper, err := record.GetValueByIndex(0) 137 | if err != nil { 138 | log.Error(err.Error()) 139 | } 140 | // Get type of the value 141 | fmt.Printf("valueWrapper type: %s \n", valueWrapper.GetType()) 142 | // Check if valueWrapper is a string type 143 | if valueWrapper.IsString() { 144 | // Convert valueWrapper to a string value 145 | v1Str, err := valueWrapper.AsString() 146 | if err != nil { 147 | log.Error(err.Error()) 148 | } 149 | fmt.Printf("Result of ValueWrapper.AsString(): %s\n", v1Str) 150 | } 151 | // Print ValueWrapper using String() 152 | fmt.Printf("Print using ValueWrapper.String(): %s", valueWrapper.String()) 153 | } 154 | // Drop space 155 | { 156 | query := "DROP SPACE IF EXISTS example_space" 157 | // Send query 158 | resultSet, err := sessionPool.Execute(query) 159 | if err != nil { 160 | fmt.Print(err.Error()) 161 | return 162 | } 163 | checkResultSet(query, resultSet) 164 | } 165 | fmt.Print("\n") 166 | log.Info("Nebula Go Client Session Pool Example Finished") 167 | } 168 | 169 | // Just a helper function to create a space for this example to run. 170 | func prepareSpace() { 171 | hostAddress := nebula.HostAddress{Host: address, Port: port} 172 | hostList := []nebula.HostAddress{hostAddress} 173 | // Create configs for connection pool using default values 174 | testPoolConfig := nebula.GetDefaultConf() 175 | testPoolConfig.UseHTTP2 = useHTTP2 176 | 177 | // Initialize connection pool 178 | pool, err := nebula.NewConnectionPool(hostList, testPoolConfig, log) 179 | if err != nil { 180 | log.Fatal(fmt.Sprintf("Fail to initialize the connection pool, host: %s, port: %d, %s", address, port, err.Error())) 181 | } 182 | // Close all connections in the pool 183 | defer pool.Close() 184 | 185 | // Create session 186 | session, err := pool.GetSession(username, password) 187 | if err != nil { 188 | log.Fatal(fmt.Sprintf("Fail to create a new session from connection pool, username: %s, password: %s, %s", 189 | username, password, err.Error())) 190 | } 191 | // Release session and return connection back to connection pool 192 | defer session.Release() 193 | 194 | checkResultSet := func(prefix string, res *nebula.ResultSet) { 195 | if !res.IsSucceed() { 196 | log.Fatal(fmt.Sprintf("%s, ErrorCode: %v, ErrorMsg: %s", prefix, res.GetErrorCode(), res.GetErrorMsg())) 197 | } 198 | } 199 | 200 | { 201 | // Prepare the query 202 | createSchema := "CREATE SPACE IF NOT EXISTS example_space(vid_type=FIXED_STRING(20)); " + 203 | "USE example_space;" + 204 | "CREATE TAG IF NOT EXISTS person(name string, age int);" + 205 | "CREATE EDGE IF NOT EXISTS like(likeness double)" 206 | 207 | // Execute a query 208 | resultSet, err := session.Execute(createSchema) 209 | if err != nil { 210 | fmt.Print(err.Error()) 211 | return 212 | } 213 | checkResultSet(createSchema, resultSet) 214 | } 215 | time.Sleep(5 * time.Second) 216 | log.Info("Space example_space was created") 217 | } 218 | -------------------------------------------------------------------------------- /examples/ssl_example/graph_client_ssl_example.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2023 vesoft inc. All rights reserved. 4 | * 5 | * This source code is licensed under Apache 2.0 License. 6 | * 7 | */ 8 | 9 | package main 10 | 11 | import ( 12 | "fmt" 13 | 14 | nebula "github.com/vesoft-inc/nebula-go/v3" 15 | ) 16 | 17 | const ( 18 | address = "127.0.0.1" 19 | // The default port of NebulaGraph 2.x is 9669. 20 | // 3699 is only for testing. 21 | port = 3699 22 | username = "root" 23 | password = "nebula" 24 | ) 25 | 26 | // Initialize logger 27 | var log = nebula.DefaultLogger{} 28 | 29 | func main() { 30 | hostAddress := nebula.HostAddress{Host: address, Port: port} 31 | hostList := []nebula.HostAddress{hostAddress} 32 | // Create configs for connection pool using default values 33 | testPoolConfig := nebula.GetDefaultConf() 34 | 35 | // Initialize connection pool 36 | sslConfig, err := nebula.GetDefaultSSLConfig( 37 | "./certs/test.3.CA.crt", 38 | "./certs/test.3.derive.crt", 39 | "./certs/test.3.derive.key", 40 | ) 41 | if err != nil { 42 | log.Fatal(fmt.Sprintf("%s.", err.Error())) 43 | } 44 | 45 | sslConfig.InsecureSkipVerify = false 46 | 47 | // Initialize connection pool 48 | pool, err := nebula.NewSslConnectionPool(hostList, testPoolConfig, sslConfig, log) 49 | if err != nil { 50 | log.Fatal(fmt.Sprintf("fail to initialize the connection pool, host: %s, port: %d, %s", address, port, err.Error())) 51 | } 52 | // Close all connections in the pool 53 | defer pool.Close() 54 | 55 | // Create session 56 | session, err := pool.GetSession(username, password) 57 | if err != nil { 58 | log.Fatal(fmt.Sprintf("Fail to create a new session from connection pool, username: %s, password: %s, %s", 59 | username, password, err.Error())) 60 | } 61 | // Release session and return connection back to connection pool 62 | defer session.Release() 63 | 64 | checkResultSet := func(prefix string, res *nebula.ResultSet) { 65 | if !res.IsSucceed() { 66 | log.Fatal(fmt.Sprintf("%s, ErrorCode: %v, ErrorMsg: %s", prefix, res.GetErrorCode(), res.GetErrorMsg())) 67 | } 68 | } 69 | 70 | { 71 | // Prepare the query 72 | createSchema := "CREATE SPACE IF NOT EXISTS basic_example_space(vid_type=FIXED_STRING(20)); " + 73 | "USE basic_example_space;" + 74 | "CREATE TAG IF NOT EXISTS person(name string, age int);" + 75 | "CREATE EDGE IF NOT EXISTS like(likeness double)" 76 | 77 | // Execute a query 78 | resultSet, err := session.Execute(createSchema) 79 | if err != nil { 80 | fmt.Print(err.Error()) 81 | return 82 | } 83 | checkResultSet(createSchema, resultSet) 84 | } 85 | // Drop space 86 | { 87 | query := "DROP SPACE IF EXISTS basic_example_space" 88 | // Send query 89 | resultSet, err := session.Execute(query) 90 | if err != nil { 91 | fmt.Print(err.Error()) 92 | return 93 | } 94 | checkResultSet(query, resultSet) 95 | } 96 | 97 | fmt.Print("\n") 98 | log.Info("Nebula Go Client Basic Example Finished") 99 | } 100 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/vesoft-inc/nebula-go/v3 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/stretchr/testify v1.10.0 7 | github.com/vesoft-inc/fbthrift v0.0.0-20230214024353-fa2f34755b28 8 | golang.org/x/net v0.33.0 9 | ) 10 | 11 | require ( 12 | github.com/davecgh/go-spew v1.1.1 // indirect 13 | github.com/pmezard/go-difflib v1.0.0 // indirect 14 | golang.org/x/text v0.21.0 // indirect 15 | gopkg.in/yaml.v3 v3.0.1 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 6 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 7 | github.com/vesoft-inc/fbthrift v0.0.0-20230214024353-fa2f34755b28 h1:gpoPCGeOEuk/TnoY9nLVK1FoBM5ie7zY3BPVG8q43ME= 8 | github.com/vesoft-inc/fbthrift v0.0.0-20230214024353-fa2f34755b28/go.mod h1:xu7e9za8StcJhBZmCDwK1Hyv4/Y0xFsjS+uqp10ECJg= 9 | golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= 10 | golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= 11 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 12 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 13 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 14 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 15 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 16 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 17 | -------------------------------------------------------------------------------- /host_address.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2020 vesoft inc. All rights reserved. 4 | * 5 | * This source code is licensed under Apache 2.0 License. 6 | * 7 | */ 8 | 9 | package nebula_go 10 | 11 | type HostAddress struct { 12 | Host string 13 | Port int 14 | } 15 | -------------------------------------------------------------------------------- /label.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2024 vesoft inc. All rights reserved. 4 | * 5 | * This source code is licensed under Apache 2.0 License. 6 | * 7 | */ 8 | package nebula_go 9 | 10 | import ( 11 | "fmt" 12 | "strings" 13 | ) 14 | 15 | type LabelName struct { 16 | Name string `nebula:"Name"` 17 | } 18 | 19 | type SpaceName struct { 20 | Name string `nebula:"Name"` 21 | } 22 | 23 | type Label struct { 24 | Field string `nebula:"Field"` 25 | Type string `nebula:"Type"` 26 | Null string `nebula:"Null"` 27 | Default string `nebula:"Default"` 28 | Comment string `nebula:"Comment"` 29 | } 30 | 31 | type LabelSchema struct { 32 | Name string 33 | Fields []LabelFieldSchema 34 | TTLDuration uint 35 | TTLCol string 36 | } 37 | 38 | type LabelFieldSchema struct { 39 | Field string 40 | Type string 41 | Nullable bool 42 | } 43 | 44 | func (tag LabelSchema) BuildCreateTagQL() string { 45 | q := "CREATE TAG IF NOT EXISTS " + tag.Name + " (" 46 | 47 | fields := []string{} 48 | for _, field := range tag.Fields { 49 | t := field.Type 50 | if t == "" { 51 | t = "string" 52 | } 53 | n := "NULL" 54 | if !field.Nullable { 55 | n = "NOT NULL" 56 | } 57 | fields = append(fields, field.Field+" "+t+" "+n) 58 | } 59 | 60 | ttl := tag.buildTTL_QL() 61 | 62 | if ttl != "" { 63 | q += strings.Join(fields, ", ") + ") " + ttl + ";" 64 | } else { 65 | q += strings.Join(fields, ", ") + ");" 66 | } 67 | 68 | return q 69 | } 70 | 71 | func (tag LabelSchema) BuildDropTagQL() string { 72 | q := "DROP TAG IF EXISTS " + tag.Name + ";" 73 | return q 74 | } 75 | 76 | func (edge LabelSchema) BuildCreateEdgeQL() string { 77 | q := "CREATE EDGE IF NOT EXISTS " + edge.Name + " (" 78 | 79 | fields := []string{} 80 | for _, field := range edge.Fields { 81 | t := field.Type 82 | if t == "" { 83 | t = "string" 84 | } 85 | n := "NULL" 86 | if !field.Nullable { 87 | n = "NOT NULL" 88 | } 89 | fields = append(fields, field.Field+" "+t+" "+n) 90 | } 91 | 92 | ttl := edge.buildTTL_QL() 93 | 94 | if ttl != "" { 95 | q += strings.Join(fields, ", ") + ") " + ttl + ";" 96 | } else { 97 | q += strings.Join(fields, ", ") + ");" 98 | } 99 | 100 | return q 101 | } 102 | 103 | func (edge LabelSchema) BuildDropEdgeQL() string { 104 | q := "DROP EDGE IF EXISTS " + edge.Name + ";" 105 | return q 106 | } 107 | 108 | func (label LabelSchema) buildTTL_QL() string { 109 | ttl := "" 110 | if label.TTLCol != "" { 111 | if !label.isTTLColValid() { 112 | panic(fmt.Errorf("TTL column %s does not exist in the fields", label.TTLCol)) 113 | } 114 | ttl = fmt.Sprintf(`TTL_DURATION = %d, TTL_COL = "%s"`, label.TTLDuration, label.TTLCol) 115 | } 116 | 117 | return ttl 118 | } 119 | 120 | func (label LabelSchema) isTTLColValid() bool { 121 | if label.TTLCol == "" { 122 | // no ttl column is valid 123 | return true 124 | } 125 | 126 | for _, field := range label.Fields { 127 | if field.Field == label.TTLCol { 128 | return true 129 | } 130 | } 131 | 132 | return false 133 | } 134 | 135 | func (field LabelFieldSchema) BuildAddTagFieldQL(labelName string) string { 136 | q := "ALTER TAG " + labelName + " ADD (" + field.Field + " " + field.Type 137 | if !field.Nullable { 138 | q += " NOT NULL" 139 | } 140 | return q + ");" 141 | } 142 | 143 | func (field LabelFieldSchema) BuildAddEdgeFieldQL(labelName string) string { 144 | q := "ALTER EDGE " + labelName + " ADD (" + field.Field + " " + field.Type 145 | if !field.Nullable { 146 | q += " NOT NULL" 147 | } 148 | return q + ");" 149 | } 150 | 151 | func (field Label) BuildDropTagFieldQL(labelName string) string { 152 | return "ALTER TAG " + labelName + " DROP (" + field.Field + ");" 153 | } 154 | 155 | func (field Label) BuildDropEdgeFieldQL(labelName string) string { 156 | return "ALTER EDGE " + labelName + " DROP (" + field.Field + ");" 157 | } 158 | -------------------------------------------------------------------------------- /label_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2024 vesoft inc. All rights reserved. 4 | * 5 | * This source code is licensed under Apache 2.0 License. 6 | * 7 | */ 8 | package nebula_go 9 | 10 | import ( 11 | "testing" 12 | 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestBuildCreateTagQL(t *testing.T) { 17 | tag := LabelSchema{ 18 | Name: "account", 19 | Fields: []LabelFieldSchema{ 20 | { 21 | Field: "name", 22 | Nullable: false, 23 | }, 24 | { 25 | Field: "email", 26 | Nullable: true, 27 | }, 28 | { 29 | Field: "phone", 30 | Nullable: true, 31 | }, 32 | }, 33 | } 34 | assert.Equal(t, "CREATE TAG IF NOT EXISTS account (name string NOT NULL, email string NULL, phone string NULL);", tag.BuildCreateTagQL()) 35 | assert.Equal(t, "DROP TAG IF EXISTS account;", tag.BuildDropTagQL()) 36 | 37 | tag.TTLDuration = 100 38 | tag.TTLCol = "created_at" 39 | assert.PanicsWithError(t, "TTL column created_at does not exist in the fields", func() { 40 | tag.BuildCreateTagQL() 41 | }) 42 | 43 | tag.Fields = append(tag.Fields, LabelFieldSchema{ 44 | Field: "created_at", 45 | Type: "timestamp", 46 | Nullable: true, 47 | }) 48 | 49 | assert.Equal(t, `CREATE TAG IF NOT EXISTS account (name string NOT NULL, email string NULL, phone string NULL, created_at timestamp NULL) TTL_DURATION = 100, TTL_COL = "created_at";`, tag.BuildCreateTagQL()) 50 | } 51 | 52 | func TestBuildCreateEdgeQL(t *testing.T) { 53 | edge := LabelSchema{ 54 | Name: "account_email", 55 | Fields: []LabelFieldSchema{ 56 | { 57 | Field: "email", 58 | Nullable: false, 59 | }, 60 | }, 61 | } 62 | assert.Equal(t, "CREATE EDGE IF NOT EXISTS account_email (email string NOT NULL);", edge.BuildCreateEdgeQL()) 63 | assert.Equal(t, "DROP EDGE IF EXISTS account_email;", edge.BuildDropEdgeQL()) 64 | 65 | edge.TTLDuration = 100 66 | edge.TTLCol = "created_at" 67 | 68 | assert.PanicsWithError(t, "TTL column created_at does not exist in the fields", func() { 69 | edge.BuildCreateEdgeQL() 70 | }) 71 | 72 | edge.Fields = append(edge.Fields, LabelFieldSchema{ 73 | Field: "created_at", 74 | Type: "timestamp", 75 | Nullable: true, 76 | }) 77 | 78 | assert.Equal(t, `CREATE EDGE IF NOT EXISTS account_email (email string NOT NULL, created_at timestamp NULL) TTL_DURATION = 100, TTL_COL = "created_at";`, edge.BuildCreateEdgeQL()) 79 | } 80 | 81 | func TestBuildAddFieldQL(t *testing.T) { 82 | field := LabelFieldSchema{ 83 | Field: "name", 84 | Type: "string", 85 | Nullable: false, 86 | } 87 | // tag 88 | assert.Equal(t, "ALTER TAG account ADD (name string NOT NULL);", field.BuildAddTagFieldQL("account")) 89 | field.Nullable = true 90 | assert.Equal(t, "ALTER TAG account ADD (name string);", field.BuildAddTagFieldQL("account")) 91 | // edge 92 | assert.Equal(t, "ALTER EDGE account ADD (name string);", field.BuildAddEdgeFieldQL("account")) 93 | field.Nullable = false 94 | assert.Equal(t, "ALTER EDGE account ADD (name string NOT NULL);", field.BuildAddEdgeFieldQL("account")) 95 | } 96 | 97 | func TestBuildDropFieldQL(t *testing.T) { 98 | field := Label{ 99 | Field: "name", 100 | } 101 | // tag 102 | assert.Equal(t, "ALTER TAG account DROP (name);", field.BuildDropTagFieldQL("account")) 103 | // edge 104 | assert.Equal(t, "ALTER EDGE account DROP (name);", field.BuildDropEdgeFieldQL("account")) 105 | } 106 | -------------------------------------------------------------------------------- /logger.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2020 vesoft inc. All rights reserved. 4 | * 5 | * This source code is licensed under Apache 2.0 License. 6 | * 7 | */ 8 | 9 | package nebula_go 10 | 11 | import ( 12 | "log" 13 | ) 14 | 15 | type Logger interface { 16 | Info(msg string) 17 | Warn(msg string) 18 | Error(msg string) 19 | Fatal(msg string) 20 | } 21 | 22 | type DefaultLogger struct{} 23 | 24 | func (l DefaultLogger) Info(msg string) { 25 | log.Printf("[INFO] %s\n", msg) 26 | } 27 | 28 | func (l DefaultLogger) Warn(msg string) { 29 | log.Printf("[WARNING] %s\n", msg) 30 | } 31 | 32 | func (l DefaultLogger) Error(msg string) { 33 | log.Printf("[ERROR] %s\n", msg) 34 | } 35 | 36 | func (l DefaultLogger) Fatal(msg string) { 37 | log.Fatalf("[FATAL] %s\n", msg) 38 | } 39 | -------------------------------------------------------------------------------- /nebula-docker-compose/.env: -------------------------------------------------------------------------------- 1 | enable_ssl=false 2 | ca_path=/secrets/root.crt 3 | password_path= 4 | cert_path=/secrets/server.crt 5 | key_path=/secrets/server.key 6 | -------------------------------------------------------------------------------- /nebula-docker-compose/docker-compose-ssl.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | metad0: 3 | image: vesoft/nebula-metad:v3 4 | environment: 5 | USER: root 6 | TZ: "${TZ}" 7 | command: 8 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 9 | - --local_ip=metad0 10 | - --ws_ip=metad0 11 | - --port=45500 12 | - --ws_http_port=11000 13 | - --data_path=/data/meta 14 | - --log_dir=/logs 15 | - --v=0 16 | - --minloglevel=0 17 | - --timezone_name=+08:00 18 | - --heartbeat_interval_secs=1 19 | # ssl 20 | - --ca_path=${ca_path} 21 | - --cert_path=${cert_path} 22 | - --key_path=${key_path} 23 | - --enable_ssl=${enable_ssl} 24 | healthcheck: 25 | test: ["CMD", "curl", "-sf", "http://metad0:11000/status"] 26 | interval: 30s 27 | timeout: 10s 28 | retries: 3 29 | start_period: 20s 30 | ports: 31 | - 45500 32 | - 11000 33 | - 11002 34 | volumes: 35 | - ./secrets:/secrets 36 | - ./data/meta0:/data/meta 37 | - ./logs/meta0:/logs 38 | networks: 39 | - nebula-net 40 | restart: on-failure 41 | cap_add: 42 | - SYS_PTRACE 43 | 44 | metad1: 45 | image: vesoft/nebula-metad:v3 46 | environment: 47 | USER: root 48 | TZ: "${TZ}" 49 | command: 50 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 51 | - --local_ip=metad1 52 | - --ws_ip=metad1 53 | - --port=45500 54 | - --ws_http_port=11000 55 | - --data_path=/data/meta 56 | - --log_dir=/logs 57 | - --v=0 58 | - --minloglevel=0 59 | - --timezone_name=+08:00 60 | - --heartbeat_interval_secs=1 61 | # ssl 62 | - --ca_path=${ca_path} 63 | - --cert_path=${cert_path} 64 | - --key_path=${key_path} 65 | - --enable_ssl=${enable_ssl} 66 | healthcheck: 67 | test: ["CMD", "curl", "-sf", "http://metad1:11000/status"] 68 | interval: 30s 69 | timeout: 10s 70 | retries: 3 71 | start_period: 20s 72 | ports: 73 | - 45500 74 | - 11000 75 | - 11002 76 | volumes: 77 | - ./secrets:/secrets 78 | - ./data/meta1:/data/meta 79 | - ./logs/meta1:/logs 80 | networks: 81 | - nebula-net 82 | restart: on-failure 83 | cap_add: 84 | - SYS_PTRACE 85 | 86 | metad2: 87 | image: vesoft/nebula-metad:v3 88 | environment: 89 | USER: root 90 | TZ: "${TZ}" 91 | command: 92 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 93 | - --local_ip=metad2 94 | - --ws_ip=metad2 95 | - --port=45500 96 | - --ws_http_port=11000 97 | - --data_path=/data/meta 98 | - --log_dir=/logs 99 | - --v=0 100 | - --minloglevel=0 101 | - --timezone_name=+08:00 102 | - --heartbeat_interval_secs=1 103 | # ssl 104 | - --ca_path=${ca_path} 105 | - --cert_path=${cert_path} 106 | - --key_path=${key_path} 107 | - --enable_ssl=${enable_ssl} 108 | healthcheck: 109 | test: ["CMD", "curl", "-sf", "http://metad2:11000/status"] 110 | interval: 30s 111 | timeout: 10s 112 | retries: 3 113 | start_period: 20s 114 | ports: 115 | - 45500 116 | - 11000 117 | - 11002 118 | volumes: 119 | - ./secrets:/secrets 120 | - ./data/meta2:/data/meta 121 | - ./logs/meta2:/logs 122 | networks: 123 | - nebula-net 124 | restart: on-failure 125 | cap_add: 126 | - SYS_PTRACE 127 | 128 | storaged0: 129 | image: vesoft/nebula-storaged:v3 130 | environment: 131 | USER: root 132 | TZ: "${TZ}" 133 | command: 134 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 135 | - --local_ip=storaged0 136 | - --ws_ip=storaged0 137 | - --port=44500 138 | - --ws_http_port=12000 139 | - --data_path=/data/storage 140 | - --log_dir=/logs 141 | - --v=0 142 | - --minloglevel=0 143 | - --timezone_name=+08:00 144 | - --heartbeat_interval_secs=1 145 | # ssl 146 | - --ca_path=${ca_path} 147 | - --cert_path=${cert_path} 148 | - --key_path=${key_path} 149 | - --enable_ssl=${enable_ssl} 150 | depends_on: 151 | - metad0 152 | - metad1 153 | - metad2 154 | healthcheck: 155 | test: ["CMD", "curl", "-sf", "http://storaged0:12000/status"] 156 | interval: 30s 157 | timeout: 10s 158 | retries: 3 159 | start_period: 20s 160 | ports: 161 | - 44500 162 | - 12000 163 | - 12002 164 | volumes: 165 | - ./secrets:/secrets 166 | - ./data/storage0:/data/storage 167 | - ./logs/storage0:/logs 168 | networks: 169 | - nebula-net 170 | restart: on-failure 171 | cap_add: 172 | - SYS_PTRACE 173 | 174 | storaged1: 175 | image: vesoft/nebula-storaged:v3 176 | environment: 177 | USER: root 178 | TZ: "${TZ}" 179 | command: 180 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 181 | - --local_ip=storaged1 182 | - --ws_ip=storaged1 183 | - --port=44500 184 | - --ws_http_port=12000 185 | - --data_path=/data/storage 186 | - --log_dir=/logs 187 | - --v=0 188 | - --minloglevel=0 189 | - --timezone_name=+08:00 190 | - --heartbeat_interval_secs=1 191 | # ssl 192 | - --ca_path=${ca_path} 193 | - --cert_path=${cert_path} 194 | - --key_path=${key_path} 195 | - --enable_ssl=${enable_ssl} 196 | depends_on: 197 | - metad0 198 | - metad1 199 | - metad2 200 | healthcheck: 201 | test: ["CMD", "curl", "-sf", "http://storaged1:12000/status"] 202 | interval: 30s 203 | timeout: 10s 204 | retries: 3 205 | start_period: 20s 206 | ports: 207 | - 44500 208 | - 12000 209 | - 12002 210 | volumes: 211 | - ./secrets:/secrets 212 | - ./data/storage1:/data/storage 213 | - ./logs/storage1:/logs 214 | networks: 215 | - nebula-net 216 | restart: on-failure 217 | cap_add: 218 | - SYS_PTRACE 219 | 220 | storaged2: 221 | image: vesoft/nebula-storaged:v3 222 | environment: 223 | USER: root 224 | TZ: "${TZ}" 225 | command: 226 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 227 | - --local_ip=storaged2 228 | - --ws_ip=storaged2 229 | - --port=44500 230 | - --ws_http_port=12000 231 | - --data_path=/data/storage 232 | - --log_dir=/logs 233 | - --v=0 234 | - --minloglevel=0 235 | - --timezone_name=+08:00 236 | - --heartbeat_interval_secs=1 237 | # ssl 238 | - --ca_path=${ca_path} 239 | - --cert_path=${cert_path} 240 | - --key_path=${key_path} 241 | - --enable_ssl=${enable_ssl} 242 | depends_on: 243 | - metad0 244 | - metad1 245 | - metad2 246 | healthcheck: 247 | test: ["CMD", "curl", "-sf", "http://storaged2:12000/status"] 248 | interval: 30s 249 | timeout: 10s 250 | retries: 3 251 | start_period: 20s 252 | ports: 253 | - 44500 254 | - 12000 255 | - 12002 256 | volumes: 257 | - ./secrets:/secrets 258 | - ./data/storage2:/data/storage 259 | - ./logs/storage2:/logs 260 | networks: 261 | - nebula-net 262 | restart: on-failure 263 | cap_add: 264 | - SYS_PTRACE 265 | 266 | graphd0: 267 | image: vesoft/nebula-graphd:v3 268 | environment: 269 | USER: root 270 | TZ: "${TZ}" 271 | command: 272 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 273 | - --port=3699 274 | - --ws_ip=graphd0 275 | - --ws_http_port=13000 276 | - --log_dir=/logs 277 | - --v=0 278 | - --minloglevel=0 279 | - --enable_authorize=true 280 | - --timezone_name=+08:00 281 | - --system_memory_high_watermark_ratio=0.99 282 | - --heartbeat_interval_secs=1 283 | # ssl 284 | - --ca_path=${ca_path} 285 | - --cert_path=${cert_path} 286 | - --key_path=${key_path} 287 | - --enable_ssl=${enable_ssl} 288 | depends_on: 289 | - metad0 290 | - metad1 291 | - metad2 292 | healthcheck: 293 | test: ["CMD", "curl", "-sf", "http://graphd0:13000/status"] 294 | interval: 30s 295 | timeout: 10s 296 | retries: 3 297 | start_period: 20s 298 | ports: 299 | - "3699:3699" 300 | - 13000 301 | - 13002 302 | volumes: 303 | - ./secrets:/secrets 304 | - ./logs/graph0:/logs 305 | networks: 306 | - nebula-net 307 | restart: on-failure 308 | cap_add: 309 | - SYS_PTRACE 310 | 311 | graphd1: 312 | image: vesoft/nebula-graphd:v3 313 | environment: 314 | USER: root 315 | TZ: "${TZ}" 316 | command: 317 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 318 | - --port=3699 319 | - --ws_ip=graphd1 320 | - --ws_http_port=13000 321 | - --log_dir=/logs 322 | - --v=0 323 | - --minloglevel=0 324 | - --enable_authorize=true 325 | - --timezone_name=+08:00 326 | - --system_memory_high_watermark_ratio=0.99 327 | - --heartbeat_interval_secs=1 328 | # ssl 329 | - --ca_path=${ca_path} 330 | - --cert_path=${cert_path} 331 | - --key_path=${key_path} 332 | - --enable_ssl=${enable_ssl} 333 | depends_on: 334 | - metad0 335 | - metad1 336 | - metad2 337 | healthcheck: 338 | test: ["CMD", "curl", "-sf", "http://graphd1:13000/status"] 339 | interval: 30s 340 | timeout: 10s 341 | retries: 3 342 | start_period: 20s 343 | ports: 344 | - "3700:3699" 345 | - 13000 346 | - 13002 347 | volumes: 348 | - ./secrets:/secrets 349 | - ./logs/graph1:/logs 350 | networks: 351 | - nebula-net 352 | restart: on-failure 353 | cap_add: 354 | - SYS_PTRACE 355 | 356 | graphd2: 357 | image: vesoft/nebula-graphd:v3 358 | environment: 359 | USER: root 360 | TZ: "${TZ}" 361 | command: 362 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 363 | - --port=3699 364 | - --ws_ip=graphd2 365 | - --ws_http_port=13000 366 | - --log_dir=/logs 367 | - --v=0 368 | - --minloglevel=0 369 | - --enable_authorize=true 370 | - --timezone_name=+08:00 371 | - --system_memory_high_watermark_ratio=0.99 372 | - --heartbeat_interval_secs=1 373 | # ssl 374 | - --ca_path=${ca_path} 375 | - --cert_path=${cert_path} 376 | - --key_path=${key_path} 377 | - --enable_ssl=${enable_ssl} 378 | 379 | 380 | depends_on: 381 | - metad0 382 | - metad1 383 | - metad2 384 | healthcheck: 385 | test: ["CMD", "curl", "-sf", "http://graphd2:13000/status"] 386 | interval: 30s 387 | timeout: 10s 388 | retries: 3 389 | start_period: 20s 390 | ports: 391 | - "3701:3699" 392 | - 13000 393 | - 13002 394 | volumes: 395 | - ./secrets:/secrets 396 | - ./logs/graph2:/logs 397 | networks: 398 | - nebula-net 399 | restart: on-failure 400 | cap_add: 401 | - SYS_PTRACE 402 | 403 | console: 404 | image: vesoft/nebula-console:nightly 405 | entrypoint: "" 406 | command: 407 | - sh 408 | - -c 409 | - | 410 | for i in `seq 1 60`;do 411 | echo "Adding hosts..." 412 | var=`nebula-console -addr graphd0 -port 3699 -u root -p nebula -enable_ssl=true \ 413 | -ssl_root_ca_path /secrets/root.crt \ 414 | -ssl_cert_path /secrets/client.crt \ 415 | -ssl_private_key_path /secrets/client.key \ 416 | --ssl_insecure_skip_verify=true \ 417 | -e 'ADD HOSTS "storaged0":44500,"storaged1":44500,"storaged2":44500'`; 418 | if [[ $$? == 0 ]];then 419 | echo "Hosts added successfully" 420 | sleep 2; 421 | nebula-console -addr graphd0 -port 3699 -u root -p nebula \ 422 | -enable_ssl=true \ 423 | -ssl_root_ca_path /secrets/root.crt \ 424 | -ssl_cert_path /secrets/client.crt \ 425 | -ssl_private_key_path /secrets/client.key \ 426 | --ssl_insecure_skip_verify=true \ 427 | -e 'CREATE SPACE session_pool(partition_num=4, replica_factor=1, vid_type = FIXED_STRING(30))' 428 | break; 429 | fi; 430 | sleep 1; 431 | echo "retry to add hosts."; 432 | done && tail -f /dev/null; 433 | volumes: 434 | - ./secrets:/secrets 435 | depends_on: 436 | - graphd0 437 | networks: 438 | - nebula-net 439 | 440 | networks: 441 | nebula-net: 442 | -------------------------------------------------------------------------------- /nebula-docker-compose/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | metad0: 3 | image: vesoft/nebula-metad:nightly 4 | environment: 5 | USER: root 6 | TZ: "${TZ}" 7 | command: 8 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 9 | - --local_ip=metad0 10 | - --ws_ip=metad0 11 | - --port=45500 12 | - --ws_http_port=11000 13 | - --data_path=/data/meta 14 | - --log_dir=/logs 15 | - --v=0 16 | - --minloglevel=0 17 | - --timezone_name=+08:00 18 | - --heartbeat_interval_secs=1 19 | # ssl 20 | - --ca_path=${ca_path} 21 | - --cert_path=${cert_path} 22 | - --key_path=${key_path} 23 | - --enable_ssl=${enable_ssl} 24 | healthcheck: 25 | test: ["CMD", "curl", "-sf", "http://metad0:11000/status"] 26 | interval: 30s 27 | timeout: 10s 28 | retries: 3 29 | start_period: 20s 30 | ports: 31 | - 45500 32 | - 11000 33 | - 11002 34 | volumes: 35 | - ./secrets:/secrets 36 | - ./data/meta0:/data/meta 37 | - ./logs/meta0:/logs 38 | networks: 39 | - nebula-net 40 | restart: on-failure 41 | cap_add: 42 | - SYS_PTRACE 43 | 44 | metad1: 45 | image: vesoft/nebula-metad:nightly 46 | environment: 47 | USER: root 48 | TZ: "${TZ}" 49 | command: 50 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 51 | - --local_ip=metad1 52 | - --ws_ip=metad1 53 | - --port=45500 54 | - --ws_http_port=11000 55 | - --data_path=/data/meta 56 | - --log_dir=/logs 57 | - --v=0 58 | - --minloglevel=0 59 | - --timezone_name=+08:00 60 | - --heartbeat_interval_secs=1 61 | # ssl 62 | - --ca_path=${ca_path} 63 | - --cert_path=${cert_path} 64 | - --key_path=${key_path} 65 | - --enable_ssl=${enable_ssl} 66 | healthcheck: 67 | test: ["CMD", "curl", "-sf", "http://metad1:11000/status"] 68 | interval: 30s 69 | timeout: 10s 70 | retries: 3 71 | start_period: 20s 72 | ports: 73 | - 45500 74 | - 11000 75 | - 11002 76 | volumes: 77 | - ./secrets:/secrets 78 | - ./data/meta1:/data/meta 79 | - ./logs/meta1:/logs 80 | networks: 81 | - nebula-net 82 | restart: on-failure 83 | cap_add: 84 | - SYS_PTRACE 85 | 86 | metad2: 87 | image: vesoft/nebula-metad:nightly 88 | environment: 89 | USER: root 90 | TZ: "${TZ}" 91 | command: 92 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 93 | - --local_ip=metad2 94 | - --ws_ip=metad2 95 | - --port=45500 96 | - --ws_http_port=11000 97 | - --data_path=/data/meta 98 | - --log_dir=/logs 99 | - --v=0 100 | - --minloglevel=0 101 | - --timezone_name=+08:00 102 | - --heartbeat_interval_secs=1 103 | # ssl 104 | - --ca_path=${ca_path} 105 | - --cert_path=${cert_path} 106 | - --key_path=${key_path} 107 | - --enable_ssl=${enable_ssl} 108 | healthcheck: 109 | test: ["CMD", "curl", "-sf", "http://metad2:11000/status"] 110 | interval: 30s 111 | timeout: 10s 112 | retries: 3 113 | start_period: 20s 114 | ports: 115 | - 45500 116 | - 11000 117 | - 11002 118 | volumes: 119 | - ./secrets:/secrets 120 | - ./data/meta2:/data/meta 121 | - ./logs/meta2:/logs 122 | networks: 123 | - nebula-net 124 | restart: on-failure 125 | cap_add: 126 | - SYS_PTRACE 127 | 128 | storaged0: 129 | image: vesoft/nebula-storaged:nightly 130 | environment: 131 | USER: root 132 | TZ: "${TZ}" 133 | command: 134 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 135 | - --local_ip=storaged0 136 | - --ws_ip=storaged0 137 | - --port=44500 138 | - --ws_http_port=12000 139 | - --data_path=/data/storage 140 | - --log_dir=/logs 141 | - --v=0 142 | - --minloglevel=0 143 | - --timezone_name=+08:00 144 | - --heartbeat_interval_secs=1 145 | # ssl 146 | - --ca_path=${ca_path} 147 | - --cert_path=${cert_path} 148 | - --key_path=${key_path} 149 | - --enable_ssl=${enable_ssl} 150 | depends_on: 151 | - metad0 152 | - metad1 153 | - metad2 154 | healthcheck: 155 | test: ["CMD", "curl", "-sf", "http://storaged0:12000/status"] 156 | interval: 30s 157 | timeout: 10s 158 | retries: 3 159 | start_period: 20s 160 | ports: 161 | - 44500 162 | - 12000 163 | - 12002 164 | volumes: 165 | - ./secrets:/secrets 166 | - ./data/storage0:/data/storage 167 | - ./logs/storage0:/logs 168 | networks: 169 | - nebula-net 170 | restart: on-failure 171 | cap_add: 172 | - SYS_PTRACE 173 | 174 | storaged1: 175 | image: vesoft/nebula-storaged:nightly 176 | environment: 177 | USER: root 178 | TZ: "${TZ}" 179 | command: 180 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 181 | - --local_ip=storaged1 182 | - --ws_ip=storaged1 183 | - --port=44500 184 | - --ws_http_port=12000 185 | - --data_path=/data/storage 186 | - --log_dir=/logs 187 | - --v=0 188 | - --minloglevel=0 189 | - --timezone_name=+08:00 190 | - --heartbeat_interval_secs=1 191 | # ssl 192 | - --ca_path=${ca_path} 193 | - --cert_path=${cert_path} 194 | - --key_path=${key_path} 195 | - --enable_ssl=${enable_ssl} 196 | depends_on: 197 | - metad0 198 | - metad1 199 | - metad2 200 | healthcheck: 201 | test: ["CMD", "curl", "-sf", "http://storaged1:12000/status"] 202 | interval: 30s 203 | timeout: 10s 204 | retries: 3 205 | start_period: 20s 206 | ports: 207 | - 44500 208 | - 12000 209 | - 12002 210 | volumes: 211 | - ./secrets:/secrets 212 | - ./data/storage1:/data/storage 213 | - ./logs/storage1:/logs 214 | networks: 215 | - nebula-net 216 | restart: on-failure 217 | cap_add: 218 | - SYS_PTRACE 219 | 220 | storaged2: 221 | image: vesoft/nebula-storaged:nightly 222 | environment: 223 | USER: root 224 | TZ: "${TZ}" 225 | command: 226 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 227 | - --local_ip=storaged2 228 | - --ws_ip=storaged2 229 | - --port=44500 230 | - --ws_http_port=12000 231 | - --data_path=/data/storage 232 | - --log_dir=/logs 233 | - --v=0 234 | - --minloglevel=0 235 | - --timezone_name=+08:00 236 | - --heartbeat_interval_secs=1 237 | # ssl 238 | - --ca_path=${ca_path} 239 | - --cert_path=${cert_path} 240 | - --key_path=${key_path} 241 | - --enable_ssl=${enable_ssl} 242 | depends_on: 243 | - metad0 244 | - metad1 245 | - metad2 246 | healthcheck: 247 | test: ["CMD", "curl", "-sf", "http://storaged2:12000/status"] 248 | interval: 30s 249 | timeout: 10s 250 | retries: 3 251 | start_period: 20s 252 | ports: 253 | - 44500 254 | - 12000 255 | - 12002 256 | volumes: 257 | - ./secrets:/secrets 258 | - ./data/storage2:/data/storage 259 | - ./logs/storage2:/logs 260 | networks: 261 | - nebula-net 262 | restart: on-failure 263 | cap_add: 264 | - SYS_PTRACE 265 | 266 | graphd0: 267 | image: vesoft/nebula-graphd:nightly 268 | container_name: nebula-docker-compose_graphd0_1 269 | environment: 270 | USER: root 271 | TZ: "${TZ}" 272 | command: 273 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 274 | - --port=3699 275 | - --ws_ip=graphd0 276 | - --ws_http_port=13000 277 | - --log_dir=/logs 278 | - --v=0 279 | - --minloglevel=0 280 | - --enable_authorize=true 281 | - --timezone_name=+08:00 282 | - --system_memory_high_watermark_ratio=0.99 283 | - --heartbeat_interval_secs=1 284 | - --max_sessions_per_ip_per_user=1200 285 | - --enable_client_white_list=true 286 | - --client_white_list=3.0.0:test 287 | # ssl 288 | - --ca_path=${ca_path} 289 | - --cert_path=${cert_path} 290 | - --key_path=${key_path} 291 | - --enable_ssl=${enable_ssl} 292 | depends_on: 293 | - metad0 294 | - metad1 295 | - metad2 296 | healthcheck: 297 | test: ["CMD", "curl", "-sf", "http://graphd0:13000/status"] 298 | interval: 30s 299 | timeout: 10s 300 | retries: 3 301 | start_period: 20s 302 | ports: 303 | - "3699:3699" 304 | - 13000 305 | - 13002 306 | volumes: 307 | - ./secrets:/secrets 308 | - ./logs/graph0:/logs 309 | networks: 310 | - nebula-net 311 | restart: on-failure 312 | cap_add: 313 | - SYS_PTRACE 314 | 315 | graphd1: 316 | image: vesoft/nebula-graphd:nightly 317 | container_name: nebula-docker-compose_graphd1_1 318 | environment: 319 | USER: root 320 | TZ: "${TZ}" 321 | command: 322 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 323 | - --port=3699 324 | - --ws_ip=graphd1 325 | - --ws_http_port=13000 326 | - --log_dir=/logs 327 | - --v=0 328 | - --minloglevel=0 329 | - --enable_authorize=true 330 | - --timezone_name=+08:00 331 | - --system_memory_high_watermark_ratio=0.99 332 | - --heartbeat_interval_secs=1 333 | - --max_sessions_per_ip_per_user=1200 334 | - --enable_client_white_list=true 335 | - --client_white_list=3.0.0:test 336 | # ssl 337 | - --ca_path=${ca_path} 338 | - --cert_path=${cert_path} 339 | - --key_path=${key_path} 340 | - --enable_ssl=${enable_ssl} 341 | depends_on: 342 | - metad0 343 | - metad1 344 | - metad2 345 | healthcheck: 346 | test: ["CMD", "curl", "-sf", "http://graphd1:13000/status"] 347 | interval: 30s 348 | timeout: 10s 349 | retries: 3 350 | start_period: 20s 351 | ports: 352 | - "3700:3699" 353 | - 13000 354 | - 13002 355 | volumes: 356 | - ./secrets:/secrets 357 | - ./logs/graph1:/logs 358 | networks: 359 | - nebula-net 360 | restart: on-failure 361 | cap_add: 362 | - SYS_PTRACE 363 | 364 | graphd2: 365 | image: vesoft/nebula-graphd:nightly 366 | container_name: nebula-docker-compose_graphd2_1 367 | environment: 368 | USER: root 369 | TZ: "${TZ}" 370 | command: 371 | - --meta_server_addrs=metad0:45500,metad1:45500,metad2:45500 372 | - --port=3699 373 | - --ws_ip=graphd2 374 | - --ws_http_port=13000 375 | - --log_dir=/logs 376 | - --v=0 377 | - --minloglevel=0 378 | - --enable_authorize=true 379 | - --timezone_name=+08:00 380 | - --system_memory_high_watermark_ratio=0.99 381 | - --heartbeat_interval_secs=1 382 | - --max_sessions_per_ip_per_user=1200 383 | - --enable_client_white_list=true 384 | - --client_white_list=3.0.0:test 385 | # ssl 386 | - --ca_path=${ca_path} 387 | - --cert_path=${cert_path} 388 | - --key_path=${key_path} 389 | - --enable_ssl=${enable_ssl} 390 | depends_on: 391 | - metad0 392 | - metad1 393 | - metad2 394 | healthcheck: 395 | test: ["CMD", "curl", "-sf", "http://graphd2:13000/status"] 396 | interval: 30s 397 | timeout: 10s 398 | retries: 3 399 | start_period: 20s 400 | ports: 401 | - "3701:3699" 402 | - 13000 403 | - 13002 404 | volumes: 405 | - ./secrets:/secrets 406 | - ./logs/graph2:/logs 407 | networks: 408 | - nebula-net 409 | restart: on-failure 410 | cap_add: 411 | - SYS_PTRACE 412 | 413 | console: 414 | image: vesoft/nebula-console:nightly 415 | entrypoint: "" 416 | command: 417 | - sh 418 | - -c 419 | - | 420 | for i in `seq 1 60`;do 421 | var=`nebula-console -addr graphd0 -port 3699 -u root -p nebula -e 'ADD HOSTS "storaged0":44500,"storaged1":44500,"storaged2":44500'`; 422 | if [[ $$? == 0 ]];then 423 | break; 424 | fi; 425 | sleep 1; 426 | echo "retry to add hosts."; 427 | done && tail -f /dev/null; 428 | depends_on: 429 | - graphd0 430 | networks: 431 | - nebula-net 432 | 433 | networks: 434 | nebula-net: 435 | -------------------------------------------------------------------------------- /nebula-docker-compose/secrets/client.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB+jCCAWOgAwIBAgIUGPV76GVV7ASOQ4OTmIrYLMUPS5YwDQYJKoZIhvcNAQEL 3 | BQAwLjELMAkGA1UEBhMCQ0gxEDAOBgNVBAoMB3Rlc3QtY2ExDTALBgNVBAMMBHJv 4 | b3QwHhcNMjMwOTE1MTAwNjQ0WhcNMzMwOTEyMTAwNjQ0WjAwMQswCQYDVQQGEwJD 5 | SDEQMA4GA1UECgwHdGVzdC1jYTEPMA0GA1UEAwwGY2xpZW50MIGfMA0GCSqGSIb3 6 | DQEBAQUAA4GNADCBiQKBgQDdmoS5JoZ+c61tsgl84hrddRZvVlsx9wL0dao6R+PK 7 | 3w3hEmSDJNPievEEY3eOKgU9PzXvcRPOuV/mXgJ47dqDwc459hss1UxrYzUCtSsK 8 | 2zPWIHksIb6b18LVSXV0hLjlH2rkz5AUT49EBpFSkSa87tNO9w0+GGzCtnavzwY9 9 | RwIDAQABoxMwETAPBgNVHREECDAGhwR/AAABMA0GCSqGSIb3DQEBCwUAA4GBAJGY 10 | AUOjNBXtMXG8HNFk8aqABFsQuwL0oE5/q7Y6HJreHmecHSZXDdOXqPiZUaKizbtJ 11 | cUFfaErn4PPSFxLIQP4CEGduuNdDxrfDJ3MRQCfY1M7ZGFWaFxkSW0EnN8ItVEcr 12 | 8umeK8H7bPq0kaskWbUl/ZjwkIsdVruYjHo7zF+X 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /nebula-docker-compose/secrets/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQDdmoS5JoZ+c61tsgl84hrddRZvVlsx9wL0dao6R+PK3w3hEmSD 3 | JNPievEEY3eOKgU9PzXvcRPOuV/mXgJ47dqDwc459hss1UxrYzUCtSsK2zPWIHks 4 | Ib6b18LVSXV0hLjlH2rkz5AUT49EBpFSkSa87tNO9w0+GGzCtnavzwY9RwIDAQAB 5 | AoGASd2YgfLCf+HlHBxtJzBizhCaQtBhI313ga666CRQXK4O/UfZMxGSgvqB3fmd 6 | v0hoaKrqOn7RoGWP/sS1REPcQtG5Zt7Al6Qojqcp8qwPOkZQQReTs939keuSyvBy 7 | klqAA/2iOCmMHVtDTvWA1PGZbp9VPgLUlKckerk1qwWecPECQQD98y5mCrb4Ywq6 8 | 24zsydARe6olbU296yepSP+eF/19nBYVEV4zLw3EwI5P0ZXtHPF7lq9uHI59TwkC 9 | DfK1HQfvAkEA32R9UQvTqgaVVmRWUTwQo+5v+dF61FuU+yx+1E4XkIY8+tTVtGNJ 10 | 1vADLZ3UKtYxlVcpm0L7ej4zO9EX0GeIKQJBAPbKAhhGZ2PhlEbdIAnpYhvrawFu 11 | RLPBOEzqVQeFVuJf8pHMzBe/rYi8fFCTZDkG/KJXYOsIM20RhraJaYmayd8CQC/K 12 | C5T+9rT025WzuVN/if/HzmSfD6vGO8TP7AH3AsuELB9s0Jracr1scwGbNfxD/i94 13 | igoQ9kNccxFk1bdbrqECQQDHPJ4H8FeXlKAVDFiKIlDQOru2JXlEMtJAW73kTdAx 14 | 12+U7lhLXAbtXyFIWTQw8ixz4c2qr5xrpc2ARfKC29BU 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /nebula-docker-compose/secrets/readme.md: -------------------------------------------------------------------------------- 1 | # readme for run.sh 2 | 3 | ```bash 4 | ./run.sh root 5 | ./run.sh server 6 | ./run.sh client 7 | ``` 8 | 9 | and then the output should be 10 | 11 | ```bash 12 | . 13 | ├── client.cnf 14 | ├── client.crt 15 | ├── client.csr 16 | ├── client.key 17 | ├── readme.md 18 | ├── root.cnf 19 | ├── root.crt 20 | ├── root.csr 21 | ├── root.key 22 | ├── root.srl 23 | ├── run.sh 24 | ├── server.cnf 25 | ├── server.crt 26 | ├── server.csr 27 | ├── server.key 28 | ``` 29 | -------------------------------------------------------------------------------- /nebula-docker-compose/secrets/root.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIChDCCAe2gAwIBAgIUT4WG4t/9uIG9JAUwmmV/C0mw4zQwDQYJKoZIhvcNAQEL 3 | BQAwLjELMAkGA1UEBhMCQ0gxEDAOBgNVBAoMB3Rlc3QtY2ExDTALBgNVBAMMBHJv 4 | b3QwHhcNMjMwOTE1MTAwNTUyWhcNMzMwOTEyMTAwNTUyWjAuMQswCQYDVQQGEwJD 5 | SDEQMA4GA1UECgwHdGVzdC1jYTENMAsGA1UEAwwEcm9vdDCBnzANBgkqhkiG9w0B 6 | AQEFAAOBjQAwgYkCgYEA4MqwdRop7iY9GpwFhncFDSvrSi7Y3jwyO1s5xocB2y+8 7 | Rwhcm8Pln4u2y8jE+oFcYFdKtbM/aol+f0KjbX0Vshws1AHgmcxtuBP0TVVK8wfp 8 | wgF28JPgkTvbFzFekmggGlTCfyQ/ehTx6j56Ti5UkLs9Q4BeErN3xShen0x0sucC 9 | AwEAAaOBnjCBmzAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBToP7wyTiF5ua6c 10 | 4uS+WIYSWtpK3TBpBgNVHSMEYjBggBToP7wyTiF5ua6c4uS+WIYSWtpK3aEypDAw 11 | LjELMAkGA1UEBhMCQ0gxEDAOBgNVBAoMB3Rlc3QtY2ExDTALBgNVBAMMBHJvb3SC 12 | FE+FhuLf/biBvSQFMJplfwtJsOM0MA0GCSqGSIb3DQEBCwUAA4GBAI6EifIhcv+z 13 | jz863TFmbJ/68kjPiiRvBplxr0kURGJGBUefutjiI4dJ1K/PcGKoTuzKI57huX9+ 14 | v0FMN/rztBJCQZbDhrObjNoaU+bBIj0msY99uCkB3HWILe31fQQv5IecvsUq38tB 15 | PWK1fn+LuzKq5AT+PNzogg4zz3JQSMgl 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /nebula-docker-compose/secrets/root.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXAIBAAKBgQDgyrB1GinuJj0anAWGdwUNK+tKLtjePDI7WznGhwHbL7xHCFyb 3 | w+Wfi7bLyMT6gVxgV0q1sz9qiX5/QqNtfRWyHCzUAeCZzG24E/RNVUrzB+nCAXbw 4 | k+CRO9sXMV6SaCAaVMJ/JD96FPHqPnpOLlSQuz1DgF4Ss3fFKF6fTHSy5wIDAQAB 5 | AoGABWfaZnaCm59/yKwH1fv2uyJbPiVyQaXg71x6PzPv731uYabp0zUayINrmery 6 | EbQarJZszYHJ/J3h1N2dYHypkNthd6G7ksFMDJm3gZVeZQaY7gZ5+Rh02JOWZvjr 7 | 0tPuS4xX5lfNhMuClvXvATazFQSteR+TvKWYH5iLebl++sECQQD3pduACRczd9I6 8 | Xcd+1cCRRMDppaF1Ssexs3CBdZzvcJqCG9vFzF4TWT+ocoiUmc2qbPiVLXuF9TRl 9 | PRYxnNvdAkEA6F9/InVRuaRfEMyQvcVgby3/49aTmRJe6S8IQkxpKoiZYlWiHiWx 10 | R95wBh4Ac1iyNhgfNxIEHnDUOi92GrqPkwJBALkpmKH1qhRAbb5YKfZKig3T002f 11 | GsahIAhcuy0ArFNW5R+NBtiwwlJDM9aVtEsvaFgZ3A9mD2qMeR2M/BAiUT0CQE4h 12 | 7gmUEQqurhhj8Dce6rH3cZcWS0Ko3qjhW5GPR705ePmLeZtRaS6VKG2mINin9iQi 13 | MDzAHgpuPa3iz39lQ2kCQCUlkHY+6sii77hkV2BOir4aG+Sp1O+tlHkHFlF5FDH5 14 | 4q3Ojbhpp7xVaAbWBcPoq9JaI04Jctb1y6A/oVfrUm0= 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /nebula-docker-compose/secrets/run.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # used to generate certs for nebula-graph 4 | # usage: 5 | # 1. ./cert.sh root 6 | # 2. ./cert.sh server 7 | # 3. ./cert.sh client 8 | 9 | # config: 10 | # server: 11 | # --cert_path=server.crt 12 | # --key_path=server.key 13 | # --ca_path=root.crt 14 | 15 | # client: follow per client repo 16 | 17 | set -eu 18 | DN_C=CH 19 | DN_O=vesoft 20 | DN_OU=Eng 21 | DN_CN= 22 | DN_EMAIL=harris.chu@xxxx.com 23 | 24 | SERVER_ADDRESS_IP="" 25 | SERVER_ADDRESS_DNS="localhost graphd0 graphd1 graphd2" 26 | CLIENT_ADDRESS_IP="" 27 | CLIENT_ADDRESS_DNS="" 28 | 29 | if [ $# != 1 ]; then 30 | echo "USAGE: $0 " 31 | exit 1; 32 | fi 33 | 34 | function gen_cert { 35 | cert_type=$1 36 | subject_name_ip=$2 37 | subject_name_dns=$3 38 | cat << EOF > ${cert_type}.cnf 39 | [ req ] 40 | default_bits = 2048 41 | prompt = no 42 | distinguished_name = dn 43 | req_extensions = req_ext 44 | 45 | [ dn ] 46 | C = CH 47 | O = test-ca 48 | CN = ${cert_type} 49 | 50 | [ v3_ca ] 51 | basicConstraints = critical,CA:TRUE 52 | subjectKeyIdentifier = hash 53 | authorityKeyIdentifier = keyid:always,issuer:always 54 | 55 | [ req_ext ] 56 | subjectAltName = @alt_names 57 | 58 | [alt_names] 59 | IP.1 = 127.0.0.1 60 | EOF 61 | if [ "$subject_name_ip" != "" ];then 62 | start=2 63 | for i in ${subject_name_ip}; do 64 | cat << EOF >> ${cert_type}.cnf 65 | IP.${start} = ${i} 66 | EOF 67 | start=$(($start+1)) 68 | done 69 | fi 70 | if [ "$subject_name_dns" != "" ];then 71 | start=1 72 | for i in ${subject_name_dns}; do 73 | cat << EOF >> ${cert_type}.cnf 74 | DNS.${start} = ${i} 75 | EOF 76 | start=$(($start+1)) 77 | done 78 | fi 79 | openssl genrsa -out ${cert_type}.key 1024 80 | openssl req -new -config ${cert_type}.cnf -out ${cert_type}.csr -key ${cert_type}.key 81 | if [ ${cert_type} == "root" ]; then 82 | openssl x509 -req -in ${cert_type}.csr -out ${cert_type}.crt -extfile ${cert_type}.cnf -extensions v3_ca -signkey ${cert_type}.key -CAcreateserial -days 3650 83 | else 84 | openssl x509 -req -in ${cert_type}.csr -out ${cert_type}.crt -CA root.crt -CAkey root.key -CAcreateserial -days 3650 -extfile ${cert_type}.cnf -extensions req_ext 85 | fi 86 | 87 | } 88 | 89 | cert_type=${1} 90 | if [ ${cert_type} != "root" ] && [ ! -e root.crt ];then 91 | echo "root.crt not exist" 92 | exit 1 93 | fi 94 | echo "generate ${cert_type} cert" 95 | if [ ${cert_type} == "server" ]; then 96 | gen_cert ${cert_type} "${SERVER_ADDRESS_IP[*]}" "${SERVER_ADDRESS_DNS[*]}" 97 | elif [ ${cert_type} == "client" ]; then 98 | gen_cert ${cert_type} "${CLIENT_ADDRESS_IP[*]}" "${CLIENT_ADDRESS_DNS[*]}" 99 | else 100 | gen_cert ${cert_type} "" "" 101 | fi 102 | echo "finish" -------------------------------------------------------------------------------- /nebula-docker-compose/secrets/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICIDCCAYmgAwIBAgIUGPV76GVV7ASOQ4OTmIrYLMUPS5cwDQYJKoZIhvcNAQEL 3 | BQAwLjELMAkGA1UEBhMCQ0gxEDAOBgNVBAoMB3Rlc3QtY2ExDTALBgNVBAMMBHJv 4 | b3QwHhcNMjMwOTE1MTAwNjQ3WhcNMzMwOTEyMTAwNjQ3WjAwMQswCQYDVQQGEwJD 5 | SDEQMA4GA1UECgwHdGVzdC1jYTEPMA0GA1UEAwwGc2VydmVyMIGfMA0GCSqGSIb3 6 | DQEBAQUAA4GNADCBiQKBgQDJxTV8cv5+3YgPft2jebm3WFPw2bsdoZMu6C3NF5By 7 | SQJaWY75KRDPx1DHqnWFhpH96gRpr9UAiBWuPrOqV5AH4ZHhqIF5Ss0OePYQvpzf 8 | C0MCt1JDqcg0692RuVDLTH1flSIdYAE2VPTygd1+DXqxxxKIASIxbW4QNq6Mc6KK 9 | pwIDAQABozkwNzA1BgNVHREELjAshwR/AAABgglsb2NhbGhvc3SCB2dyYXBoZDCC 10 | B2dyYXBoZDGCB2dyYXBoZDIwDQYJKoZIhvcNAQELBQADgYEAY8yRSFEzV+wxNdMG 11 | 3745FqntuKXTPzL2trWfgHnbGjqqoECiEn2D/h2F2zVboUz+F06CW66XaIXP4iac 12 | Ff6YNWP9eVaO2xzSDeGkZ2ME5gNgiXc2taZipBDwlp8fm0bH7RhExJn3dSVKtP6Q 13 | Ikk2wvBDxc3NKXkXKxKM1sDjyLs= 14 | -----END CERTIFICATE----- 15 | -------------------------------------------------------------------------------- /nebula-docker-compose/secrets/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQDJxTV8cv5+3YgPft2jebm3WFPw2bsdoZMu6C3NF5BySQJaWY75 3 | KRDPx1DHqnWFhpH96gRpr9UAiBWuPrOqV5AH4ZHhqIF5Ss0OePYQvpzfC0MCt1JD 4 | qcg0692RuVDLTH1flSIdYAE2VPTygd1+DXqxxxKIASIxbW4QNq6Mc6KKpwIDAQAB 5 | AoGABH99+sFBhSiBIP9OB5fuGPVCIctozXdNpa0NYFYXJUUVZVg7xLgypL8nwNu+ 6 | 9PQUCxoNcSG3WOSvvwusy65aCpjNchd6jTBjBUrvBla/ZS94BUsTOOkEDnC5RZHn 7 | qhMlrUDfvVIObUOux+jxjbGRVBlLPmyqjiuCZGV0IiW2oKkCQQDl9K5SEmvmWdCh 8 | OOpvHwSskPzOOHwICh3zvhfW0D2vIrggr+6sDrTbQJSSpelmyb169SQY4xHwmasz 9 | 1vO4AR8rAkEA4J9SXYFVa2wdFYlpSQ4So99FrLvc95t9sCgvjzr+UNNP7B/LNlto 10 | Zj826Ho6kMjXzV5yd2gnXH2AMpXIa5fkdQJBAMS1wIEYnRCZ+CxSQcj44ci05m2K 11 | SB+gd+rPzBjIXlv4+UWM+kBY/EEqR24DW1vAa8RI+64GYIxPB/L6h9X5r60CQBlq 12 | DVClDHwd/GBubqokXHmMDn8Ptl2RizP+J/tlqlaMwhMuObjJuNMwvc6p3ax5/Oiw 13 | kfTupN1zGakfh1CdG+0CQQDfhISnuYZSko5KpfUKbe8MOMbEXl9CopMe15Kp2YAf 14 | /ylMfZ3FzF2/Aj6dOoetLXhypS3Jxu58WHHg64L64Vvf 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /nebula/constants.go: -------------------------------------------------------------------------------- 1 | // Autogenerated by Thrift Compiler (facebook) 2 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 3 | // @generated 4 | 5 | package nebula 6 | 7 | import ( 8 | "bytes" 9 | "context" 10 | "sync" 11 | "fmt" 12 | thrift "github.com/vesoft-inc/fbthrift/thrift/lib/go/thrift" 13 | ) 14 | 15 | // (needed to ensure safety because of naive import list construction.) 16 | var _ = thrift.ZERO 17 | var _ = fmt.Printf 18 | var _ = sync.Mutex{} 19 | var _ = bytes.Equal 20 | var _ = context.Background 21 | 22 | const Version = "3.0.0" 23 | 24 | func init() { 25 | } 26 | 27 | -------------------------------------------------------------------------------- /nebula/graph/constants.go: -------------------------------------------------------------------------------- 1 | // Autogenerated by Thrift Compiler (facebook) 2 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 3 | // @generated 4 | 5 | package graph 6 | 7 | import ( 8 | "bytes" 9 | "context" 10 | "sync" 11 | "fmt" 12 | thrift "github.com/vesoft-inc/fbthrift/thrift/lib/go/thrift" 13 | nebula0 "github.com/vesoft-inc/nebula-go/v3/nebula" 14 | 15 | ) 16 | 17 | // (needed to ensure safety because of naive import list construction.) 18 | var _ = thrift.ZERO 19 | var _ = fmt.Printf 20 | var _ = sync.Mutex{} 21 | var _ = bytes.Equal 22 | var _ = context.Background 23 | 24 | var _ = nebula0.GoUnusedProtection__ 25 | 26 | func init() { 27 | } 28 | 29 | -------------------------------------------------------------------------------- /nebula/meta/constants.go: -------------------------------------------------------------------------------- 1 | // Autogenerated by Thrift Compiler (facebook) 2 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 3 | // @generated 4 | 5 | package meta 6 | 7 | import ( 8 | "bytes" 9 | "context" 10 | "sync" 11 | "fmt" 12 | thrift "github.com/vesoft-inc/fbthrift/thrift/lib/go/thrift" 13 | nebula0 "github.com/vesoft-inc/nebula-go/v3/nebula" 14 | 15 | ) 16 | 17 | // (needed to ensure safety because of naive import list construction.) 18 | var _ = thrift.ZERO 19 | var _ = fmt.Printf 20 | var _ = sync.Mutex{} 21 | var _ = bytes.Equal 22 | var _ = context.Background 23 | 24 | var _ = nebula0.GoUnusedProtection__ 25 | var const_lit_vid_type_type_length int16 = 8 26 | 27 | func init() { 28 | } 29 | 30 | -------------------------------------------------------------------------------- /nebula/storage/constants.go: -------------------------------------------------------------------------------- 1 | // Autogenerated by Thrift Compiler (facebook) 2 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 3 | // @generated 4 | 5 | package storage 6 | 7 | import ( 8 | "bytes" 9 | "context" 10 | "sync" 11 | "fmt" 12 | thrift "github.com/vesoft-inc/fbthrift/thrift/lib/go/thrift" 13 | nebula0 "github.com/vesoft-inc/nebula-go/v3/nebula" 14 | meta1 "github.com/vesoft-inc/nebula-go/v3/nebula/meta" 15 | 16 | ) 17 | 18 | // (needed to ensure safety because of naive import list construction.) 19 | var _ = thrift.ZERO 20 | var _ = fmt.Printf 21 | var _ = sync.Mutex{} 22 | var _ = bytes.Equal 23 | var _ = context.Background 24 | 25 | var _ = nebula0.GoUnusedProtection__ 26 | var _ = meta1.GoUnusedProtection__ 27 | 28 | func init() { 29 | } 30 | 31 | -------------------------------------------------------------------------------- /schema_manager.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2024 vesoft inc. All rights reserved. 4 | * 5 | * This source code is licensed under Apache 2.0 License. 6 | * 7 | */ 8 | 9 | package nebula_go 10 | 11 | import ( 12 | "fmt" 13 | "log" 14 | "strings" 15 | ) 16 | 17 | type SchemaManager struct { 18 | pool *SessionPool 19 | verbose bool 20 | } 21 | 22 | func NewSchemaManager(pool *SessionPool) *SchemaManager { 23 | return &SchemaManager{pool: pool} 24 | } 25 | 26 | func (mgr *SchemaManager) WithVerbose(verbose bool) *SchemaManager { 27 | mgr.verbose = verbose 28 | return mgr 29 | } 30 | 31 | // ApplyTag applies the given tag to the graph. 32 | // 1. If the tag does not exist, it will be created. 33 | // 2. If the tag exists, it will be checked if the fields are the same. 34 | // 2.1 If not, the new fields will be added. 35 | // 2.2 If the field type is different, it will return an error. 36 | // 2.3 If a field exists in the graph but not in the given tag, 37 | // it will be removed. 38 | // 3. If the tag exists and the fields are the same, 39 | // it will be checked if the TTL is set as expected. 40 | // 41 | // Notice: 42 | // We won't change the field type because it has 43 | // unexpected behavior for the data. 44 | func (mgr *SchemaManager) ApplyTag(tag LabelSchema) (*ResultSet, error) { 45 | // 1. Make sure the tag exists 46 | fields, err := mgr.pool.DescTag(tag.Name) 47 | if err != nil { 48 | // If the tag does not exist, create it 49 | if strings.Contains(err.Error(), ErrorTagNotFound) { 50 | if mgr.verbose { 51 | log.Printf("ApplyTag: create the not existing tag. name=%s\n", tag.Name) 52 | } 53 | return mgr.pool.CreateTag(tag) 54 | } 55 | return nil, err 56 | } 57 | 58 | // 2. The tag exists, add new fields if needed 59 | // 2.1 Prepare the new fields 60 | addFieldQLs := []string{} 61 | for _, expected := range tag.Fields { 62 | found := false 63 | for _, actual := range fields { 64 | if expected.Field == actual.Field { 65 | found = true 66 | // 2.2 Check if the field type is different 67 | if expected.Type != actual.Type { 68 | return nil, fmt.Errorf("field type is different. "+ 69 | "Expected: %s, Actual: %s", expected.Type, actual.Type) 70 | } 71 | break 72 | } 73 | } 74 | if !found { 75 | // 2.3 Add the not exists field QL 76 | q := expected.BuildAddTagFieldQL(tag.Name) 77 | addFieldQLs = append(addFieldQLs, q) 78 | } 79 | } 80 | // 2.4 Execute the add field QLs if needed 81 | if len(addFieldQLs) > 0 { 82 | queries := strings.Join(addFieldQLs, " ") 83 | if mgr.verbose { 84 | log.Printf("ApplyTag: add the not existing fields. name=%s queries=%s\n", tag.Name, queries) 85 | } 86 | _, err = mgr.pool.ExecuteAndCheck(queries) 87 | if err != nil { 88 | return nil, err 89 | } 90 | } 91 | 92 | // 3. Remove the not expected field if needed 93 | // 3.1 Prepare the not expected fields 94 | dropFieldQLs := []string{} 95 | for _, actual := range fields { 96 | redundant := true 97 | for _, expected := range tag.Fields { 98 | if expected.Field == actual.Field { 99 | redundant = false 100 | break 101 | } 102 | } 103 | if redundant { 104 | // 3.2 Remove the not expected field 105 | q := actual.BuildDropTagFieldQL(tag.Name) 106 | dropFieldQLs = append(dropFieldQLs, q) 107 | } 108 | } 109 | // 3.3 Execute the drop field QLs if needed 110 | if len(dropFieldQLs) > 0 { 111 | queries := strings.Join(dropFieldQLs, " ") 112 | if mgr.verbose { 113 | log.Printf("ApplyTag: remove the not expected fields. name=%s queries=%s\n", tag.Name, queries) 114 | } 115 | _, err := mgr.pool.ExecuteAndCheck(queries) 116 | if err != nil { 117 | return nil, err 118 | } 119 | } 120 | 121 | // 4. Check if the TTL is set as expected. 122 | ttlCol, ttlDuration, err := mgr.pool.GetTagTTL(tag.Name) 123 | if err != nil { 124 | return nil, err 125 | } 126 | 127 | if ttlCol != tag.TTLCol || ttlDuration != tag.TTLDuration { 128 | if mgr.verbose { 129 | log.Printf( 130 | "ApplyTag: alter the tag TTL. name=%s, col from %s to %s, duration from %d to %d\n", 131 | tag.Name, 132 | ttlCol, 133 | tag.TTLCol, 134 | ttlDuration, 135 | tag.TTLDuration, 136 | ) 137 | } 138 | 139 | _, err = mgr.pool.AddTagTTL(tag.Name, tag.TTLCol, tag.TTLDuration) 140 | if err != nil { 141 | return nil, err 142 | } 143 | } 144 | 145 | return nil, nil 146 | } 147 | 148 | // ApplyEdge applies the given edge to the graph. 149 | // 1. If the edge does not exist, it will be created. 150 | // 2. If the edge exists, it will be checked if the fields are the same. 151 | // 2.1 If not, the new fields will be added. 152 | // 2.2 If the field type is different, it will return an error. 153 | // 2.3 If a field exists in the graph but not in the given edge, 154 | // it will be removed. 155 | // 3. If the edge exists and the fields are the same, 156 | // it will be checked if the TTL is set as expected. 157 | // 158 | // Notice: 159 | // We won't change the field type because it has 160 | // unexpected behavior for the data. 161 | func (mgr *SchemaManager) ApplyEdge(edge LabelSchema) (*ResultSet, error) { 162 | // 1. Make sure the edge exists 163 | fields, err := mgr.pool.DescEdge(edge.Name) 164 | if err != nil { 165 | // If the edge does not exist, create it 166 | if strings.Contains(err.Error(), ErrorEdgeNotFound) { 167 | if mgr.verbose { 168 | log.Printf("ApplyEdge: create the not existing edge. name=%s\n", edge.Name) 169 | } 170 | return mgr.pool.CreateEdge(edge) 171 | } 172 | return nil, err 173 | } 174 | 175 | // 2. The edge exists now, add new fields if needed 176 | // 2.1 Prepare the new fields 177 | addFieldQLs := []string{} 178 | for _, expected := range edge.Fields { 179 | found := false 180 | for _, actual := range fields { 181 | if expected.Field == actual.Field { 182 | found = true 183 | // 2.2 Check if the field type is different 184 | if expected.Type != actual.Type { 185 | return nil, fmt.Errorf("field type is different. "+ 186 | "Expected: %s, Actual: %s", expected.Type, actual.Type) 187 | } 188 | break 189 | } 190 | } 191 | if !found { 192 | // 2.3 Add the not exists field QL 193 | q := expected.BuildAddEdgeFieldQL(edge.Name) 194 | addFieldQLs = append(addFieldQLs, q) 195 | } 196 | } 197 | // 2.4 Execute the add field QLs if needed 198 | if len(addFieldQLs) > 0 { 199 | queries := strings.Join(addFieldQLs, " ") 200 | if mgr.verbose { 201 | log.Printf("ApplyEdge: add the not existing fields. name=%s queries=%s\n", edge.Name, queries) 202 | } 203 | _, err := mgr.pool.ExecuteAndCheck(queries) 204 | if err != nil { 205 | return nil, err 206 | } 207 | } 208 | 209 | // 3. Remove the not expected field if needed 210 | // 3.1 Prepare the not expected fields 211 | dropFieldQLs := []string{} 212 | for _, actual := range fields { 213 | redundant := true 214 | for _, expected := range edge.Fields { 215 | if expected.Field == actual.Field { 216 | redundant = false 217 | break 218 | } 219 | } 220 | if redundant { 221 | // 3.2 Remove the not expected field 222 | q := actual.BuildDropEdgeFieldQL(edge.Name) 223 | dropFieldQLs = append(dropFieldQLs, q) 224 | } 225 | } 226 | // 3.3 Execute the drop field QLs if needed 227 | if len(dropFieldQLs) > 0 { 228 | queries := strings.Join(dropFieldQLs, "") 229 | if mgr.verbose { 230 | log.Printf("ApplyEdge: remove the not expected fields. name=%s queries=%s\n", edge.Name, queries) 231 | } 232 | _, err := mgr.pool.ExecuteAndCheck(queries) 233 | if err != nil { 234 | return nil, err 235 | } 236 | } 237 | 238 | // 4. Check if the TTL is set as expected. 239 | ttlCol, ttlDuration, err := mgr.pool.GetEdgeTTL(edge.Name) 240 | if err != nil { 241 | return nil, err 242 | } 243 | 244 | if ttlCol != edge.TTLCol || ttlDuration != edge.TTLDuration { 245 | if mgr.verbose { 246 | log.Printf( 247 | "ApplyEdge: alter the edge TTL. name=%s, col from %s to %s, duration from %d to %d\n", 248 | edge.Name, 249 | ttlCol, 250 | edge.TTLCol, 251 | ttlDuration, 252 | edge.TTLDuration, 253 | ) 254 | } 255 | 256 | _, err = mgr.pool.AddEdgeTTL(edge.Name, edge.TTLCol, edge.TTLDuration) 257 | if err != nil { 258 | return nil, err 259 | } 260 | } 261 | 262 | return nil, nil 263 | } 264 | -------------------------------------------------------------------------------- /session.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2020 vesoft inc. All rights reserved. 4 | * 5 | * This source code is licensed under Apache 2.0 License. 6 | * 7 | */ 8 | 9 | package nebula_go 10 | 11 | import ( 12 | "fmt" 13 | "sync" 14 | 15 | "github.com/vesoft-inc/nebula-go/v3/nebula" 16 | graph "github.com/vesoft-inc/nebula-go/v3/nebula/graph" 17 | ) 18 | 19 | type timezoneInfo struct { 20 | offset int32 21 | name []byte 22 | } 23 | 24 | type Session struct { 25 | sessionID int64 26 | connection *connection 27 | connPool *ConnectionPool // the connection pool which the session belongs to. could be nil if the Session is store in the SessionPool 28 | log Logger 29 | mu sync.Mutex 30 | timezoneInfo 31 | } 32 | 33 | func (session *Session) reconnectWithExecuteErr(err error) error { 34 | if _err := session.reConnect(); _err != nil { 35 | return fmt.Errorf("failed to reconnect, %s", _err.Error()) 36 | } 37 | session.log.Info(fmt.Sprintf("Successfully reconnect to host: %s, port: %d", 38 | session.connection.severAddress.Host, session.connection.severAddress.Port)) 39 | return nil 40 | } 41 | 42 | func (session *Session) executeWithReconnect(f func() (interface{}, error)) (interface{}, error) { 43 | resp, err := f() 44 | if err == nil { 45 | return resp, nil 46 | } 47 | if err2 := session.reconnectWithExecuteErr(err); err2 != nil { 48 | return nil, err2 49 | } 50 | // Execute with the new connection 51 | return f() 52 | } 53 | 54 | // ExecuteWithParameter returns the result of the given query as a ResultSet 55 | func (session *Session) ExecuteWithParameter(stmt string, params map[string]interface{}) (*ResultSet, error) { 56 | session.mu.Lock() 57 | defer session.mu.Unlock() 58 | paramsMap, err := parseParams(params) 59 | if err != nil { 60 | return nil, err 61 | } 62 | 63 | fn := func() (*graph.ExecutionResponse, error) { 64 | return session.connection.executeWithParameter(session.sessionID, stmt, paramsMap) 65 | } 66 | return session.tryExecuteLocked(fn) 67 | 68 | } 69 | 70 | // Execute returns the result of the given query as a ResultSet 71 | func (session *Session) Execute(stmt string) (*ResultSet, error) { 72 | return session.ExecuteWithParameter(stmt, map[string]interface{}{}) 73 | } 74 | 75 | func (session *Session) tryExecuteLocked(fn func() (*graph.ExecutionResponse, error)) (*ResultSet, error) { 76 | if session.connection == nil { 77 | return nil, fmt.Errorf("failed to execute: Session has been released") 78 | } 79 | execFunc := func() (interface{}, error) { 80 | resp, err := fn() 81 | if err != nil { 82 | return nil, err 83 | } 84 | resSet, err := genResultSet(resp, session.timezoneInfo) 85 | if err != nil { 86 | return nil, err 87 | } 88 | return resSet, nil 89 | } 90 | resp, err := session.executeWithReconnect(execFunc) 91 | if err != nil { 92 | return nil, err 93 | } 94 | return resp.(*ResultSet), err 95 | } 96 | 97 | func (session *Session) ExecuteWithTimeout(stmt string, timeoutMs int64) (*ResultSet, error) { 98 | return session.ExecuteWithParameterTimeout(stmt, map[string]interface{}{}, timeoutMs) 99 | } 100 | 101 | func (session *Session) ExecuteWithParameterTimeout(stmt string, params map[string]interface{}, timeoutMs int64) (*ResultSet, error) { 102 | session.mu.Lock() 103 | defer session.mu.Unlock() 104 | if timeoutMs <= 0 { 105 | return nil, fmt.Errorf("timeout should be a positive number") 106 | } 107 | paramsMap, err := parseParams(params) 108 | if err != nil { 109 | return nil, err 110 | } 111 | 112 | fn := func() (*graph.ExecutionResponse, error) { 113 | return session.connection.executeWithParameterTimeout(session.sessionID, stmt, paramsMap, timeoutMs) 114 | } 115 | return session.tryExecuteLocked(fn) 116 | } 117 | 118 | // ExecuteJson returns the result of the given query as a json string 119 | // Date and Datetime will be returned in UTC 120 | // 121 | // JSON struct: 122 | // 123 | // { 124 | // "results":[ 125 | // { 126 | // "columns":[ 127 | // ], 128 | // "data":[ 129 | // { 130 | // "row":[ 131 | // "row-data" 132 | // ], 133 | // "meta":[ 134 | // "metadata" 135 | // ] 136 | // } 137 | // ], 138 | // "latencyInUs":0, 139 | // "spaceName":"", 140 | // "planDesc ":{ 141 | // "planNodeDescs":[ 142 | // { 143 | // "name":"", 144 | // "id":0, 145 | // "outputVar":"", 146 | // "description":{ 147 | // "key":"" 148 | // }, 149 | // "profiles":[ 150 | // { 151 | // "rows":1, 152 | // "execDurationInUs":0, 153 | // "totalDurationInUs":0, 154 | // "otherStats":{} 155 | // } 156 | // ], 157 | // "branchInfo":{ 158 | // "isDoBranch":false, 159 | // "conditionNodeId":-1 160 | // }, 161 | // "dependencies":[] 162 | // } 163 | // ], 164 | // "nodeIndexMap":{}, 165 | // "format":"", 166 | // "optimize_time_in_us":0 167 | // }, 168 | // "comment ":"" 169 | // } 170 | // ], 171 | // "errors":[ 172 | // { 173 | // "code": 0, 174 | // "message": "" 175 | // } 176 | // ] 177 | // } 178 | func (session *Session) ExecuteJson(stmt string) ([]byte, error) { 179 | return session.ExecuteJsonWithParameter(stmt, map[string]interface{}{}) 180 | } 181 | 182 | // ExecuteJson returns the result of the given query as a json string 183 | // Date and Datetime will be returned in UTC 184 | // The result is a JSON string in the same format as ExecuteJson() 185 | func (session *Session) ExecuteJsonWithParameter(stmt string, params map[string]interface{}) ([]byte, error) { 186 | session.mu.Lock() 187 | defer session.mu.Unlock() 188 | if session.connection == nil { 189 | return nil, fmt.Errorf("failed to execute: Session has been released") 190 | } 191 | 192 | paramsMap := make(map[string]*nebula.Value) 193 | for k, v := range params { 194 | nv, er := value2Nvalue(v) 195 | if er != nil { 196 | return nil, er 197 | } 198 | paramsMap[k] = nv 199 | } 200 | execFunc := func() (interface{}, error) { 201 | resp, err := session.connection.ExecuteJsonWithParameter(session.sessionID, stmt, paramsMap) 202 | if err != nil { 203 | return nil, err 204 | } 205 | return resp, nil 206 | } 207 | resp, err := session.executeWithReconnect(execFunc) 208 | if err != nil { 209 | return nil, err 210 | } 211 | return resp.([]byte), err 212 | } 213 | 214 | func (session *Session) ExecuteAndCheck(stmt string) (*ResultSet, error) { 215 | rs, err := session.Execute(stmt) 216 | if err != nil { 217 | return nil, err 218 | } 219 | 220 | if !rs.IsSucceed() { 221 | errMsg := rs.GetErrorMsg() 222 | return nil, fmt.Errorf("fail to execute query. %s", errMsg) 223 | } 224 | 225 | return rs, nil 226 | } 227 | 228 | type SpaceConf struct { 229 | Name string 230 | Partition uint 231 | Replica uint 232 | VidType string 233 | IgnoreIfExists bool 234 | Comment string 235 | } 236 | 237 | func (session *Session) CreateSpace(conf SpaceConf) (*ResultSet, error) { 238 | if conf.Partition == 0 { 239 | conf.Partition = 100 240 | } 241 | if conf.Replica == 0 { 242 | conf.Replica = 1 243 | } 244 | if conf.VidType == "" { 245 | conf.VidType = "FIXED_STRING(8)" 246 | } 247 | 248 | var q string 249 | if conf.IgnoreIfExists { 250 | q = fmt.Sprintf( 251 | "CREATE SPACE IF NOT EXISTS %s (partition_num = %d, replica_factor = %d, vid_type = %s)", 252 | conf.Name, 253 | conf.Partition, 254 | conf.Replica, 255 | conf.VidType, 256 | ) 257 | } else { 258 | q = fmt.Sprintf( 259 | "CREATE SPACE %s (partition_num = %d, replica_factor = %d, vid_type = %s)", 260 | conf.Name, 261 | conf.Partition, 262 | conf.Replica, 263 | conf.VidType, 264 | ) 265 | } 266 | 267 | if conf.Comment != "" { 268 | q += fmt.Sprintf(` COMMENT = "%s"`, conf.Comment) 269 | } 270 | 271 | return session.ExecuteAndCheck(q + ";") 272 | } 273 | 274 | func (session *Session) ShowSpaces() ([]SpaceName, error) { 275 | rs, err := session.ExecuteAndCheck("SHOW SPACES;") 276 | if err != nil { 277 | return nil, err 278 | } 279 | 280 | var names []SpaceName 281 | rs.Scan(&names) 282 | 283 | return names, nil 284 | } 285 | 286 | func (session *Session) reConnect() error { 287 | newConnection, err := session.connPool.getIdleConn() 288 | if err != nil { 289 | return err 290 | } 291 | 292 | session.connPool.deactivate(session.connection, false, false) 293 | session.connection = newConnection 294 | return nil 295 | } 296 | 297 | // Release logs out and releases connection hold by session. 298 | // The connection will be added into the activeConnectionQueue of the connection pool 299 | // so that it could be reused. 300 | func (session *Session) Release() { 301 | if session == nil { 302 | return 303 | } 304 | session.mu.Lock() 305 | defer session.mu.Unlock() 306 | if session.connection == nil { 307 | session.log.Warn("Session has been released") 308 | return 309 | } 310 | if err := session.connection.signOut(session.sessionID); err != nil { 311 | session.log.Warn(fmt.Sprintf("Sign out failed, %s", err.Error())) 312 | } 313 | 314 | // if the session is created from the connection pool, return the connection to the pool 315 | if session.connPool != nil { 316 | session.connPool.release(session.connection) 317 | } 318 | session.connection = nil 319 | } 320 | 321 | func (session *Session) GetSessionID() int64 { 322 | return session.sessionID 323 | } 324 | 325 | func IsError(resp *graph.ExecutionResponse) bool { 326 | return resp.GetErrorCode() != nebula.ErrorCode_SUCCEEDED 327 | } 328 | 329 | // Ping checks if the session is valid 330 | func (session *Session) Ping() error { 331 | if session.connection == nil { 332 | return fmt.Errorf("failed to ping: Session has been released") 333 | } 334 | // send ping request 335 | resp, err := session.Execute(`RETURN "NEBULA GO PING"`) 336 | // check connection level error 337 | if err != nil { 338 | return fmt.Errorf("session ping failed, %s" + err.Error()) 339 | } 340 | // check session level error 341 | if !resp.IsSucceed() { 342 | return fmt.Errorf("session ping failed, %s" + resp.GetErrorMsg()) 343 | } 344 | return nil 345 | } 346 | 347 | // construct Slice to nebula.NList 348 | func slice2Nlist(list []interface{}) (*nebula.NList, error) { 349 | sv := []*nebula.Value{} 350 | var ret nebula.NList 351 | for _, item := range list { 352 | nv, er := value2Nvalue(item) 353 | if er != nil { 354 | return nil, er 355 | } 356 | sv = append(sv, nv) 357 | } 358 | ret.Values = sv 359 | return &ret, nil 360 | } 361 | 362 | // construct map to nebula.NMap 363 | func map2Nmap(m map[string]interface{}) (*nebula.NMap, error) { 364 | var ret nebula.NMap 365 | kvs, err := parseParams(m) 366 | if err != nil { 367 | return nil, err 368 | } 369 | ret.Kvs = kvs 370 | return &ret, nil 371 | } 372 | 373 | // construct go-type to nebula.Value 374 | func value2Nvalue(param interface{}) (value *nebula.Value, err error) { 375 | value = nebula.NewValue() 376 | switch v := param.(type) { 377 | case bool: 378 | value.BVal = &v 379 | case int: 380 | ival := int64(v) 381 | value.IVal = &ival 382 | case int64: 383 | value.IVal = &v 384 | case float64: 385 | if v == float64(int64(v)) { 386 | iv := int64(v) 387 | value.IVal = &iv 388 | } else { 389 | value.FVal = &v 390 | } 391 | case float32: 392 | if v == float32(int64(v)) { 393 | iv := int64(v) 394 | value.IVal = &iv 395 | } else { 396 | fval := float64(v) 397 | value.FVal = &fval 398 | } 399 | case string: 400 | value.SVal = []byte(v) 401 | case nil: 402 | nval := nebula.NullType___NULL__ 403 | value.NVal = &nval 404 | case []interface{}: 405 | nv, er := slice2Nlist(v) 406 | if er != nil { 407 | err = er 408 | } 409 | value.LVal = nv 410 | case map[string]interface{}: 411 | nv, er := map2Nmap(v) 412 | if er != nil { 413 | err = er 414 | } 415 | value.MVal = nv 416 | case nebula.Value: 417 | value = &v 418 | case nebula.Date: 419 | value.SetDVal(&v) 420 | case nebula.DateTime: 421 | value.SetDtVal(&v) 422 | case nebula.Duration: 423 | value.SetDuVal(&v) 424 | case nebula.Time: 425 | value.SetTVal(&v) 426 | case nebula.Geography: 427 | value.SetGgVal(&v) 428 | default: 429 | // unsupported other Value type, use this function carefully 430 | err = fmt.Errorf("only support convert boolean/float/int/int64/string/map/list to nebula.Value but %T", param) 431 | } 432 | return 433 | } 434 | -------------------------------------------------------------------------------- /session_test.go: -------------------------------------------------------------------------------- 1 | //go:build integration 2 | // +build integration 3 | 4 | /* Copyright (c) 2021 vesoft inc. All rights reserved. 5 | * 6 | * This source code is licensed under Apache 2.0 License. 7 | */ 8 | 9 | package nebula_go 10 | 11 | import ( 12 | "context" 13 | "testing" 14 | "time" 15 | 16 | "github.com/stretchr/testify/assert" 17 | ) 18 | 19 | func TestSession_Execute(t *testing.T) { 20 | config := GetDefaultConf() 21 | host := HostAddress{address, port} 22 | pool, err := NewConnectionPool([]HostAddress{host}, config, DefaultLogger{}) 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | errCh := make(chan error, 1) 27 | 28 | f := func(s *Session) { 29 | time.Sleep(10 * time.Microsecond) 30 | reps, err := s.Execute("yield 1") 31 | if err != nil { 32 | errCh <- err 33 | } 34 | if !reps.IsSucceed() { 35 | t.Fatal(reps.resp.ErrorMsg) 36 | } 37 | 38 | // test Ping() 39 | err = s.Ping() 40 | if err != nil { 41 | errCh <- err 42 | } 43 | } 44 | ctx, cancel := context.WithTimeout(context.TODO(), 300*time.Millisecond) 45 | defer cancel() 46 | go func(ctx context.Context) { 47 | sess, err := pool.GetSession("root", "nebula") 48 | if err != nil { 49 | errCh <- err 50 | } 51 | for { 52 | select { 53 | case <-ctx.Done(): 54 | break 55 | default: 56 | f(sess) 57 | } 58 | } 59 | }(ctx) 60 | go func(ctx context.Context) { 61 | sess, err := pool.GetSession("root", "nebula") 62 | if err != nil { 63 | errCh <- err 64 | } 65 | for { 66 | select { 67 | case <-ctx.Done(): 68 | default: 69 | f(sess) 70 | } 71 | } 72 | }(ctx) 73 | 74 | for { 75 | select { 76 | case err := <-errCh: 77 | t.Fatal(err) 78 | case <-ctx.Done(): 79 | return 80 | } 81 | } 82 | 83 | } 84 | 85 | func TestSession_Recover(t *testing.T) { 86 | query := "show hosts" 87 | config := GetDefaultConf() 88 | host := HostAddress{address, port} 89 | pool, err := NewConnectionPool([]HostAddress{host}, config, DefaultLogger{}) 90 | if err != nil { 91 | t.Fatal(err) 92 | } 93 | 94 | sess, err := pool.GetSession("root", "nebula") 95 | if err != nil { 96 | t.Fatal(err) 97 | } 98 | assert.Equal(t, 1, pool.getActiveConnCount()+pool.getIdleConnCount()) 99 | go func() { 100 | for { 101 | _, _ = sess.Execute(query) 102 | } 103 | }() 104 | stopContainer(t, "nebula-docker-compose_graphd0_1") 105 | stopContainer(t, "nebula-docker-compose_graphd1_1") 106 | stopContainer(t, "nebula-docker-compose_graphd2_1") 107 | defer func() { 108 | startContainer(t, "nebula-docker-compose_graphd1_1") 109 | startContainer(t, "nebula-docker-compose_graphd2_1") 110 | }() 111 | <-time.After(3 * time.Second) 112 | startContainer(t, "nebula-docker-compose_graphd0_1") 113 | <-time.After(3 * time.Second) 114 | _, err = sess.Execute(query) 115 | if err != nil { 116 | t.Fatal(err) 117 | } 118 | assert.Equal(t, 1, pool.getActiveConnCount()+pool.getIdleConnCount()) 119 | } 120 | 121 | func TestSession_CreateSpace_ShowSpaces(t *testing.T) { 122 | config := GetDefaultConf() 123 | host := HostAddress{address, port} 124 | pool, err := NewConnectionPool([]HostAddress{host}, config, DefaultLogger{}) 125 | if err != nil { 126 | t.Fatal(err) 127 | } 128 | 129 | sess, err := pool.GetSession("root", "nebula") 130 | if err != nil { 131 | t.Fatal(err) 132 | } 133 | assert.Equal(t, 1, pool.getActiveConnCount()+pool.getIdleConnCount()) 134 | 135 | newSpaceName := "new_created_space" 136 | conf := SpaceConf{ 137 | Name: newSpaceName, 138 | Partition: 1, 139 | Replica: 1, 140 | VidType: "FIXED_STRING(12)", 141 | } 142 | 143 | _, err = sess.CreateSpace(conf) 144 | if err != nil { 145 | t.Fatal(err) 146 | } 147 | 148 | conf.IgnoreIfExists = true 149 | // Create again should work 150 | _, err = sess.CreateSpace(conf) 151 | if err != nil { 152 | t.Fatal(err) 153 | } 154 | 155 | spaceNames, err := sess.ShowSpaces() 156 | if err != nil { 157 | t.Fatal(err) 158 | } 159 | var names []string 160 | for _, space := range spaceNames { 161 | names = append(names, space.Name) 162 | } 163 | assert.LessOrEqual(t, 1, len(names)) 164 | assert.Contains(t, names, newSpaceName) 165 | } 166 | -------------------------------------------------------------------------------- /ssl_connection_test.go: -------------------------------------------------------------------------------- 1 | //go:build integration 2 | // +build integration 3 | 4 | /* 5 | * 6 | * Copyright (c) 2020 vesoft inc. All rights reserved. 7 | * 8 | * This source code is licensed under Apache 2.0 License. 9 | * 10 | */ 11 | 12 | package nebula_go 13 | 14 | import ( 15 | "os" 16 | "testing" 17 | "time" 18 | ) 19 | 20 | func TestSslConnection(t *testing.T) { 21 | // skip test when ssl_test is not set to true 22 | skipSsl(t) 23 | 24 | hostAddress := HostAddress{Host: address, Port: port} 25 | hostList := []HostAddress{} 26 | hostList = append(hostList, hostAddress) 27 | 28 | testPoolConfig = PoolConfig{ 29 | TimeOut: 0 * time.Millisecond, 30 | IdleTime: 0 * time.Millisecond, 31 | MaxConnPoolSize: 10, 32 | MinConnPoolSize: 1, 33 | } 34 | 35 | sslConfig, err := GetDefaultSSLConfig( 36 | "./nebula-docker-compose/secrets/root.crt", 37 | "./nebula-docker-compose/secrets/client.crt", 38 | "./nebula-docker-compose/secrets/client.key", 39 | ) 40 | if err != nil { 41 | t.Fatal(err) 42 | } 43 | 44 | sslConfig.InsecureSkipVerify = false 45 | 46 | // Initialize connection pool 47 | pool, err := NewSslConnectionPool(hostList, testPoolConfig, sslConfig, nebulaLog) 48 | if err != nil { 49 | t.Fatalf("fail to initialize the connection pool, host: %s, port: %d, %s", address, port, err.Error()) 50 | } 51 | // Close all connections in the pool 52 | defer pool.Close() 53 | 54 | // Create session 55 | session, err := pool.GetSession(username, password) 56 | if err != nil { 57 | t.Fatalf("fail to create a new session from connection pool, username: %s, password: %s, %s", 58 | username, password, err.Error()) 59 | } 60 | defer session.Release() 61 | // Execute a query 62 | resp, err := tryToExecute(session, "SHOW HOSTS;") 63 | if err != nil { 64 | t.Fatalf(err.Error()) 65 | return 66 | } 67 | checkResultSet(t, "show hosts", resp) 68 | // Create a new space 69 | resp, err = tryToExecute(session, "CREATE SPACE client_test(partition_num=1024, replica_factor=1, vid_type = FIXED_STRING(30));") 70 | if err != nil { 71 | t.Fatalf(err.Error()) 72 | return 73 | } 74 | checkResultSet(t, "create space", resp) 75 | 76 | resp, err = tryToExecute(session, "DROP SPACE client_test;") 77 | if err != nil { 78 | t.Fatalf(err.Error()) 79 | return 80 | } 81 | checkResultSet(t, "drop space", resp) 82 | } 83 | 84 | // TODO: generate certificate with hostName info and disable InsecureSkipVerify 85 | func TestSslConnectionSelfSigned(t *testing.T) { 86 | // skip test when ssl_test is not set to true 87 | skipSslSelfSigned(t) 88 | 89 | hostAddress := HostAddress{Host: address, Port: port} 90 | hostList := []HostAddress{} 91 | hostList = append(hostList, hostAddress) 92 | 93 | testPoolConfig = PoolConfig{ 94 | TimeOut: 0 * time.Millisecond, 95 | IdleTime: 0 * time.Millisecond, 96 | MaxConnPoolSize: 10, 97 | MinConnPoolSize: 1, 98 | } 99 | 100 | sslConfig, err := GetDefaultSSLConfig( 101 | "./nebula-docker-compose/secrets/root.crt", 102 | "./nebula-docker-compose/secrets/client.crt", 103 | "./nebula-docker-compose/secrets/client.key", 104 | ) 105 | if err != nil { 106 | t.Fatal(err) 107 | } 108 | 109 | sslConfig.InsecureSkipVerify = false 110 | 111 | // Initialize connection pool 112 | pool, err := NewSslConnectionPool(hostList, testPoolConfig, sslConfig, nebulaLog) 113 | if err != nil { 114 | t.Fatalf("fail to initialize the connection pool, host: %s, port: %d, %s", address, port, err.Error()) 115 | } 116 | // Close all connections in the pool 117 | defer pool.Close() 118 | 119 | // Create session 120 | session, err := pool.GetSession(username, password) 121 | if err != nil { 122 | t.Fatalf("fail to create a new session from connection pool, username: %s, password: %s, %s", 123 | username, password, err.Error()) 124 | } 125 | defer session.Release() 126 | // Execute a query 127 | resp, err := tryToExecute(session, "SHOW HOSTS;") 128 | if err != nil { 129 | t.Fatalf(err.Error()) 130 | return 131 | } 132 | checkResultSet(t, "show hosts", resp) 133 | // Create a new space 134 | resp, err = tryToExecute(session, "CREATE SPACE client_test(partition_num=1024, replica_factor=1, vid_type = FIXED_STRING(30));") 135 | if err != nil { 136 | t.Fatalf(err.Error()) 137 | return 138 | } 139 | checkResultSet(t, "create space", resp) 140 | 141 | resp, err = tryToExecute(session, "DROP SPACE client_test;") 142 | if err != nil { 143 | t.Fatalf(err.Error()) 144 | return 145 | } 146 | checkResultSet(t, "drop space", resp) 147 | } 148 | 149 | func openAndReadFileTest(t *testing.T, path string) []byte { 150 | b, err := openAndReadFile(path) 151 | if err != nil { 152 | t.Fatal(err) 153 | } 154 | return b 155 | } 156 | 157 | func skipSsl(t *testing.T) { 158 | if os.Getenv("ssl_test") != "true" { 159 | t.Skip("Skipping SSL testing in CI environment") 160 | } 161 | } 162 | 163 | func skipSslSelfSigned(t *testing.T) { 164 | if os.Getenv("self_signed") != "true" { 165 | t.Skip("Skipping SSL testing in CI environment") 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /ssl_sessionpool_test.go: -------------------------------------------------------------------------------- 1 | //go:build integration 2 | // +build integration 3 | 4 | /* 5 | * 6 | * Copyright (c) 2023 vesoft inc. All rights reserved. 7 | * 8 | * This source code is licensed under Apache 2.0 License. 9 | * 10 | */ 11 | 12 | package nebula_go 13 | 14 | import ( 15 | "testing" 16 | "time" 17 | ) 18 | 19 | func TestSslSessionPool(t *testing.T) { 20 | skipSsl(t) 21 | 22 | hostAddress := HostAddress{Host: address, Port: port} 23 | hostList := []HostAddress{} 24 | hostList = append(hostList, hostAddress) 25 | sslConfig, err := GetDefaultSSLConfig( 26 | "./nebula-docker-compose/secrets/root.crt", 27 | "./nebula-docker-compose/secrets/client.crt", 28 | "./nebula-docker-compose/secrets/client.key", 29 | ) 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | sslConfig.InsecureSkipVerify = true // This is only used for testing 34 | conf, err := NewSessionPoolConf( 35 | username, 36 | password, 37 | hostList, 38 | "session_pool", 39 | WithMaxSize(10), 40 | WithMinSize(1), 41 | WithTimeOut(0*time.Millisecond), 42 | WithIdleTime(0*time.Millisecond), 43 | WithSSLConfig(sslConfig), 44 | ) 45 | 46 | if err != nil { 47 | t.Fatal(err) 48 | } 49 | pool, err := NewSessionPool(*conf, nebulaLog) 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | defer pool.Close() 54 | resp, err := pool.Execute("SHOW HOSTS;") 55 | if err != nil { 56 | t.Fatalf(err.Error()) 57 | return 58 | } 59 | checkResultSet(t, "show hosts", resp) 60 | // Create a new space 61 | resp, err = pool.Execute("CREATE SPACE client_test(partition_num=1024, replica_factor=1, vid_type = FIXED_STRING(30));") 62 | if err != nil { 63 | t.Fatalf(err.Error()) 64 | return 65 | } 66 | checkResultSet(t, "create space", resp) 67 | 68 | resp, err = pool.Execute("DROP SPACE client_test;") 69 | if err != nil { 70 | t.Fatalf(err.Error()) 71 | return 72 | } 73 | checkResultSet(t, "drop space", resp) 74 | } 75 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | package nebula_go 2 | 3 | import ( 4 | "regexp" 5 | "strconv" 6 | ) 7 | 8 | func IndexOf(collection []string, element string) int { 9 | for i, item := range collection { 10 | if item == element { 11 | return i 12 | } 13 | } 14 | 15 | return -1 16 | } 17 | 18 | func parseTTL(s string) (string, uint, error) { 19 | col, err := parseTTLCol(s) 20 | if err != nil { 21 | return "", 0, err 22 | } 23 | 24 | duration, err := parseTTLDuration(s) 25 | if err != nil { 26 | return "", 0, err 27 | } 28 | 29 | return col, duration, nil 30 | } 31 | 32 | func parseTTLCol(s string) (string, error) { 33 | reg, err := regexp.Compile(`ttl_col = "(\w+)"`) 34 | ss := reg.FindStringSubmatch(s) 35 | 36 | if err != nil { 37 | return "", err 38 | } 39 | 40 | if len(ss) == 2 { 41 | return ss[1], nil 42 | } 43 | 44 | return "", nil 45 | } 46 | 47 | func parseTTLDuration(s string) (uint, error) { 48 | reg, err := regexp.Compile(`ttl_duration = (\d+)`) 49 | 50 | if err != nil { 51 | return 0, err 52 | } 53 | 54 | ss := reg.FindStringSubmatch(s) 55 | 56 | if len(ss) == 2 { 57 | ttl, err := strconv.Atoi(ss[1]) 58 | if err != nil { 59 | return 0, err 60 | } 61 | return uint(ttl), nil 62 | } 63 | 64 | return 0, nil 65 | } 66 | -------------------------------------------------------------------------------- /util_test.go: -------------------------------------------------------------------------------- 1 | package nebula_go 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestUtil_IndexOf(t *testing.T) { 10 | collection := []string{"a", "b", "c"} 11 | assert.Equal(t, IndexOf(collection, "a"), 0) 12 | assert.Equal(t, IndexOf(collection, "b"), 1) 13 | assert.Equal(t, IndexOf(collection, "c"), 2) 14 | assert.Equal(t, IndexOf(collection, "d"), -1) 15 | } 16 | 17 | func TestUtil_parseTTL(t *testing.T) { 18 | s := "CREATE TAG `user` (\n\t\t`name` string NOT NULL,\n\t\t`created_at` int64 NULL\n\t) ttl_duration = 5, ttl_col = \"created_at\"" 19 | col, duration, err := parseTTL(s) 20 | assert.Nil(t, err) 21 | assert.Equal(t, col, "created_at") 22 | assert.Equal(t, duration, uint(5)) 23 | 24 | s = "CREATE TAG `user` (\n\t\t`name` string NOT NULL,\n\t\t`created_at` int64 NULL\n\t) ttl_duration = 0, ttl_col = \"\"" 25 | col, duration, err = parseTTL(s) 26 | assert.Nil(t, err) 27 | assert.Equal(t, col, "") 28 | assert.Equal(t, duration, uint(0)) 29 | } 30 | --------------------------------------------------------------------------------