├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── boring-cyborg.yml ├── dependabot.yml └── workflows │ ├── api.yml │ ├── release.yml │ ├── release_package.yml │ └── ui.yml ├── .gitignore ├── .go-version ├── .goreleaser.yml ├── .mergify.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── UPGRADE-0.3.0.md ├── bin ├── docker-compose ├── install_etcd_ubuntu.sh ├── install_rabbitmq_ubuntu.sh ├── local_etcd_mac.sh ├── local_etcd_ubuntu.sh ├── release.sh └── test_script.sh ├── cache └── .gitignore ├── cmd ├── api.go ├── configure.go ├── deploy.go ├── destroy.go ├── license.go ├── list.go ├── root.go └── version.go ├── config.dist.yml ├── config.prod.yml ├── config.toml ├── core ├── controller │ ├── health.go │ ├── job.go │ ├── metrics.go │ ├── ready.go │ ├── service.go │ ├── tag.go │ └── workers.go ├── definition │ ├── base.go │ ├── cassandra.go │ ├── cassandra_test.go │ ├── clickhouse.go │ ├── consul.go │ ├── consul_test.go │ ├── elasticsearch.go │ ├── elasticsearch_test.go │ ├── etcd.go │ ├── etcd_test.go │ ├── etherpad.go │ ├── ghost.go │ ├── grafana.go │ ├── grafana_test.go │ ├── graphite.go │ ├── graphite_test.go │ ├── httpbin.go │ ├── httpbin_test.go │ ├── jaeger.go │ ├── jaeger_test.go │ ├── jenkins.go │ ├── kafka.go │ ├── mailhog.go │ ├── mailhog_test.go │ ├── mariadb.go │ ├── mariadb_test.go │ ├── memcached.go │ ├── memcached_test.go │ ├── minio.go │ ├── minio_test.go │ ├── mongodb.go │ ├── mongodb_test.go │ ├── mysql.go │ ├── mysql_test.go │ ├── nagios.go │ ├── nagios_test.go │ ├── postgresql.go │ ├── postgresql_test.go │ ├── prometheus.go │ ├── prometheus_test.go │ ├── rabbitmq.go │ ├── rabbitmq_test.go │ ├── redis.go │ ├── redis_test.go │ ├── registry.go │ ├── vault.go │ ├── vault_test.go │ ├── zipkin.go │ └── zipkin_test.go ├── driver │ ├── db_interface.go │ ├── etcd.go │ ├── etcd_integration_test.go │ └── etcd_mock.go ├── middleware │ ├── auth.go │ ├── correlation.go │ ├── cors.go │ ├── log.go │ └── metric.go ├── model │ ├── README.md │ ├── job.go │ ├── job_test.go │ ├── metric.go │ ├── option.go │ ├── option_test.go │ ├── service.go │ └── service_test.go ├── runtime │ ├── base.go │ ├── docker_compose.go │ └── docker_compose_test.go ├── service │ ├── docker_hub.go │ ├── docker_hub_test.go │ ├── fuzzy_finder.go │ ├── http.go │ ├── http_test.go │ ├── prometheus.go │ └── prompt.go └── util │ ├── helpers.go │ ├── helpers_test.go │ ├── map.go │ ├── map_test.go │ ├── shell.go │ └── shell_test.go ├── definition ├── README.md └── docker-compose.yml ├── deployment ├── .gitkeep └── linux │ ├── install.sh │ └── upgrade.sh ├── go.mod ├── go.sum ├── peanut.go ├── pkg ├── loader.go └── server_mock.go ├── renovate.json ├── sdk ├── job.go └── service.go ├── static ├── chart.drawio ├── chart.png ├── logo.png ├── screenshot_01.png ├── screenshot_02.png ├── screenshot_03.png └── screenshot_04.png └── web ├── .env.dist ├── .gitignore ├── .prettierrc.json ├── babel.config.js ├── dist ├── css │ ├── 673.a1f88a72.css │ ├── app.e9dfbb89.css │ └── chunk-vendors.053b6b6e.css ├── img │ └── logo.b0d08bb0.png ├── index.html ├── js │ ├── 167.893f9c3f.js │ ├── 167.893f9c3f.js.map │ ├── 469.f08c0560.js │ ├── 469.f08c0560.js.map │ ├── 673.dc1c89fc.js │ ├── 673.dc1c89fc.js.map │ ├── 757.3e4a3a28.js │ ├── 757.3e4a3a28.js.map │ ├── 881.3f5d3418.js │ ├── 881.3f5d3418.js.map │ ├── app.82fc2188.js │ ├── app.82fc2188.js.map │ ├── chunk-vendors.e5622ebe.js │ └── chunk-vendors.e5622ebe.js.map └── logo.png ├── package-lock.json ├── package.json ├── public ├── index.html └── logo.png └── src ├── App.vue ├── assets └── logo.png ├── common ├── api.service.js └── service.api.js ├── components └── .gitkeep ├── main.js ├── router └── index.js ├── store ├── index.js └── service.module.js └── views ├── Deploy.vue ├── Home.vue ├── Login.vue ├── NotFound.vue └── Services.vue /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Docs: https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners 2 | * @clivern 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: # clivern 2 | custom: clivern.com/sponsor/ 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **Development or production environment** 11 | - OS: [e.g. Ubuntu 18.04] 12 | - Go 1.13 13 | 14 | **Additional context** 15 | Add any other context about the problem here. 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Additional context** 14 | Add any other context or screenshots about the feature request here. 15 | -------------------------------------------------------------------------------- /.github/boring-cyborg.yml: -------------------------------------------------------------------------------- 1 | --- 2 | firstIssueWelcomeComment: "Thanks for opening your first issue here! Be sure to follow the issue template!" 3 | firstPRMergeComment: "Awesome work, congrats on your first merged pull request!" 4 | firstPRWelcomeComment: "Thanks for opening this pull request! Please check out our contributing guidelines." 5 | 6 | labelPRBasedOnFilePath: 7 | "🚧 CI": 8 | - .github/workflows/* 9 | 10 | "🚧 CSS": 11 | - "**/*.css" 12 | 13 | "🚧 Configuration": 14 | - .github/* 15 | 16 | "🚧 Dependencies": 17 | - Dockerfile* 18 | - composer.* 19 | - package.json 20 | - package-lock.json 21 | - yarn.lock 22 | - go.mod 23 | - go.sum 24 | - build.gradle 25 | - Cargo.toml 26 | - Cargo.lock 27 | - Gemfile.lock 28 | - Gemfile 29 | 30 | "🚧 Docker": 31 | - Dockerfile* 32 | 33 | - .docker/**/* 34 | 35 | "🚧 Documentation": 36 | - README.md 37 | - CONTRIBUTING.md 38 | 39 | "🚧 Go": 40 | - "**/*.go" 41 | 42 | "🚧 Rust": 43 | - "**/*.rs" 44 | 45 | "🚧 Java": 46 | - "**/*.java" 47 | 48 | "🚧 Ruby": 49 | - "**/*.rb" 50 | 51 | "🚧 HTML": 52 | - "**/*.htm" 53 | - "**/*.html" 54 | 55 | "🚧 Image": 56 | - "**/*.gif" 57 | - "**/*.jpg" 58 | - "**/*.jpeg" 59 | - "**/*.png" 60 | - "**/*.webp" 61 | 62 | "🚧 JSON": 63 | - "**/*.json" 64 | 65 | "🚧 JavaScript": 66 | - "**/*.js" 67 | - package.json 68 | - package-lock.json 69 | - yarn.lock 70 | 71 | "🚧 MarkDown": 72 | - "**/*.md" 73 | 74 | "🚧 PHP": 75 | - "**/*.php" 76 | - composer.* 77 | 78 | "🚧 Source": 79 | - src/**/* 80 | 81 | "🚧 TOML": 82 | - "**/*.toml" 83 | 84 | "🚧 Templates": 85 | 86 | - "**/*.twig" 87 | - "**/*.tpl" 88 | 89 | "🚧 Tests": 90 | - tests/**/* 91 | 92 | "🚧 YAML": 93 | - "**/*.yml" 94 | - "**/*.yaml" 95 | 96 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gomod" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/api.yml: -------------------------------------------------------------------------------- 1 | name: API 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | go: ["1.18", "1.18.1", "1.19", "1.20", "1.21", "1.22", "1.23"] 14 | name: Go ${{ matrix.go }} run 15 | steps: 16 | - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 17 | - name: Setup go 18 | uses: actions/setup-go@v5 19 | with: 20 | go-version: ${{ matrix.go }} 21 | 22 | - name: Get dependencies 23 | run: | 24 | export PATH=${PATH}:`go env GOPATH`/bin 25 | make install_revive 26 | 27 | - name: Install etcd server 28 | run: | 29 | bash ./bin/local_etcd_ubuntu.sh 30 | 31 | - name: Run make ci 32 | run: | 33 | export PATH=${PATH}:`go env GOPATH`/bin 34 | go get -t . 35 | make ci 36 | make integration 37 | make integration 38 | git status 39 | git diff > diff.log 40 | cat diff.log 41 | git clean -fd 42 | git reset --hard 43 | make verify 44 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: Checkout 14 | uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 15 | with: 16 | fetch-depth: 0 17 | - 18 | name: Set up Go 19 | uses: actions/setup-go@v5 20 | with: 21 | go-version: 1.19 22 | - 23 | name: Run GoReleaser 24 | uses: goreleaser/goreleaser-action@v4 25 | with: 26 | version: latest 27 | args: release --rm-dist 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /.github/workflows/release_package.yml: -------------------------------------------------------------------------------- 1 | name: Release Package 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: Checkout 14 | uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 15 | with: 16 | fetch-depth: 0 17 | - 18 | name: Set up Go 19 | uses: actions/setup-go@v5 20 | with: 21 | go-version: 1.19 22 | 23 | - name: Update checksum database 24 | run: | 25 | ./bin/release.sh 26 | -------------------------------------------------------------------------------- /.github/workflows/ui.yml: -------------------------------------------------------------------------------- 1 | name: UI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | build: 9 | runs-on: ${{ matrix.os }} 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | os: [ubuntu-latest] 14 | node-version: [14.x, 15.x, 16.x, 18.x] 15 | 16 | steps: 17 | - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 18 | 19 | - name: Cache node modules 20 | uses: actions/cache@v4 21 | with: 22 | path: ~/.npm 23 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 24 | restore-keys: | 25 | ${{ runner.os }}-node- 26 | - name: Node ${{ matrix.node-version }} 27 | uses: actions/setup-node@v4 28 | with: 29 | node-version: ${{ matrix.node-version }} 30 | 31 | - name: Install vuejs cli 32 | run: npm install -g @vue/cli@5.0.4 33 | 34 | - name: Run build ui 35 | run: make build_ui 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # dist dir 15 | dist 16 | 17 | !web/dist 18 | 19 | sync 20 | 21 | *.db 22 | -------------------------------------------------------------------------------- /.go-version: -------------------------------------------------------------------------------- 1 | 1.20.4 2 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # Make sure to check the documentation at http://goreleaser.com 2 | --- 3 | archives: 4 | - 5 | replacements: 6 | 386: i386 7 | amd64: x86_64 8 | darwin: Darwin 9 | linux: Linux 10 | windows: Windows 11 | files: 12 | - LICENSE 13 | - README.md 14 | - config.dist.yml 15 | - config.prod.yml 16 | before: 17 | hooks: 18 | - "go mod download" 19 | - "go generate ./..." 20 | builds: 21 | - 22 | env: 23 | - CGO_ENABLED=0 24 | goos: 25 | - linux 26 | - darwin 27 | - windows 28 | changelog: 29 | filters: 30 | exclude: 31 | - "^docs:" 32 | - "^test:" 33 | sort: asc 34 | checksum: 35 | name_template: checksums.txt 36 | snapshot: 37 | name_template: "{{ .Tag }}-next" 38 | project_name: peanut 39 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pull_request_rules: 3 | - 4 | actions: 5 | merge: 6 | method: squash 7 | conditions: 8 | - author!=Clivern 9 | - approved-reviews-by=Clivern 10 | - label=release 11 | name: "Automatic Merge 🚀" 12 | - 13 | actions: 14 | merge: 15 | method: merge 16 | conditions: 17 | - author=Clivern 18 | - label=release 19 | name: "Automatic Merge 🚀" 20 | - 21 | actions: 22 | merge: 23 | method: squash 24 | conditions: 25 | - "author=renovate[bot]" 26 | - label=release 27 | name: "Automatic Merge for Renovate PRs 🚀" 28 | - 29 | actions: 30 | comment: 31 | message: "Nice! PR merged successfully." 32 | conditions: 33 | - merged 34 | name: "Merge Done 🚀" 35 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at hello@clivern.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | - With issues: 4 | - Use the search tool before opening a new issue. 5 | - Please provide source code and commit sha if you found a bug. 6 | - Review existing issues and provide feedback or react to them. 7 | 8 | - With pull requests: 9 | - Open your pull request against `main` 10 | - Your pull request should have no more than two commits, if not you should squash them. 11 | - It should pass all tests in the available continuous integrations systems such as TravisCI. 12 | - You should add/modify tests to cover your proposed code changes. 13 | - If your pull request contains a new feature, please document it on the README. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Clivern 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | go ?= go 2 | gofmt ?= $(go)fmt 3 | npm ?= npm 4 | npx ?= npx 5 | pkgs = ./... 6 | 7 | 8 | help: Makefile 9 | @echo 10 | @echo " Choose a command run in Peanut:" 11 | @echo 12 | @sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /' 13 | @echo 14 | 15 | 16 | ## install_revive: Install revive for linting. 17 | .PHONY: install_revive 18 | install_revive: 19 | @echo ">> ============= Install Revive ============= <<" 20 | $(go) install github.com/mgechev/revive@v1.3.9 21 | 22 | 23 | ## style: Check code style. 24 | .PHONY: style 25 | style: 26 | @echo ">> ============= Checking Code Style ============= <<" 27 | @fmtRes=$$($(gofmt) -d $$(find . -path ./vendor -prune -o -name '*.go' -print)); \ 28 | if [ -n "$${fmtRes}" ]; then \ 29 | echo "gofmt checking failed!"; echo "$${fmtRes}"; echo; \ 30 | echo "Please ensure you are using $$($(go) version) for formatting code."; \ 31 | exit 1; \ 32 | fi 33 | 34 | 35 | ## check_license: Check if license header on all files. 36 | .PHONY: check_license 37 | check_license: 38 | @echo ">> ============= Checking License Header ============= <<" 39 | @licRes=$$(for file in $$(find . -type f -iname '*.go' ! -path './vendor/*') ; do \ 40 | awk 'NR<=3' $$file | grep -Eq "(Copyright|generated|GENERATED)" || echo $$file; \ 41 | done); \ 42 | if [ -n "$${licRes}" ]; then \ 43 | echo "license header checking failed:"; echo "$${licRes}"; \ 44 | exit 1; \ 45 | fi 46 | 47 | 48 | ## test_short: Run test cases with short flag. 49 | .PHONY: test_short 50 | test_short: 51 | @echo ">> ============= Running Short Tests ============= <<" 52 | $(go) clean -testcache 53 | $(go) test -mod=readonly -short $(pkgs) 54 | 55 | 56 | ## test: Run test cases. 57 | .PHONY: test 58 | test: 59 | @echo ">> ============= Running All Tests ============= <<" 60 | $(go) clean -testcache 61 | $(go) test -mod=readonly -run=Unit -bench=. -benchmem -v -cover $(pkgs) 62 | 63 | 64 | ## integration: Run integration test cases (Requires etcd) 65 | .PHONY: integration 66 | integration: 67 | @echo ">> ============= Running All Tests ============= <<" 68 | $(go) clean -testcache 69 | $(go) test -mod=readonly -run=Integration -bench=. -benchmem -v -cover $(pkgs) 70 | 71 | 72 | ## lint: Lint the code. 73 | .PHONY: lint 74 | lint: 75 | @echo ">> ============= Lint All Files ============= <<" 76 | revive -config config.toml -exclude vendor/... -formatter friendly ./... 77 | 78 | 79 | ## verify: Verify dependencies 80 | .PHONY: verify 81 | verify: 82 | @echo ">> ============= List Dependencies ============= <<" 83 | $(go) list -m all 84 | @echo ">> ============= Verify Dependencies ============= <<" 85 | $(go) mod verify 86 | 87 | 88 | ## format: Format the code. 89 | .PHONY: format 90 | format: 91 | @echo ">> ============= Formatting Code ============= <<" 92 | $(go) fmt $(pkgs) 93 | 94 | 95 | ## vet: Examines source code and reports suspicious constructs. 96 | .PHONY: vet 97 | vet: 98 | @echo ">> ============= Vetting Code ============= <<" 99 | $(go) vet $(pkgs) 100 | 101 | 102 | ## coverage: Create HTML coverage report 103 | .PHONY: coverage 104 | coverage: 105 | @echo ">> ============= Coverage ============= <<" 106 | rm -f coverage.html cover.out 107 | $(go) test -mod=readonly -coverprofile=cover.out $(pkgs) 108 | go tool cover -html=cover.out -o coverage.html 109 | 110 | 111 | ## serve_ui: Serve admin dashboard 112 | .PHONY: serve_ui 113 | serve_ui: 114 | @echo ">> ============= Run Vuejs App ============= <<" 115 | cd web;$(npm) run serve 116 | 117 | 118 | ## build_ui: Builds admin dashboard for production 119 | .PHONY: build_ui 120 | build_ui: 121 | @echo ">> ============= Build Vuejs App ============= <<" 122 | cd web;$(npm) install;$(npm) run build 123 | 124 | 125 | ## check_ui_format: Check dashboard code format 126 | .PHONY: check_ui_format 127 | check_ui_format: 128 | @echo ">> ============= Validate js format ============= <<" 129 | cd web;$(npx) prettier --check . 130 | 131 | 132 | ## format_ui: Format dashboard code 133 | .PHONY: format_ui 134 | format_ui: 135 | @echo ">> ============= Format js Code ============= <<" 136 | cd web;$(npx) prettier --write . 137 | 138 | 139 | ## package: Package assets 140 | .PHONY: package 141 | package: 142 | @echo ">> ============= Package Assets ============= <<" 143 | -rm $(shell pwd)/web/.env 144 | echo "VUE_APP_API_URL=" > $(shell pwd)/web/.env.dist 145 | cd web;$(npm) run build 146 | 147 | 148 | ## run: Run the API Server 149 | .PHONY: run 150 | run: 151 | @echo ">> ============= Run Tower ============= <<" 152 | $(go) run peanut.go api -c config.dist.yml 153 | 154 | 155 | ## ci: Run all CI tests. 156 | .PHONY: ci 157 | ci: style check_license test vet lint 158 | @echo "\n==> All quality checks passed" 159 | 160 | 161 | .PHONY: help 162 | -------------------------------------------------------------------------------- /UPGRADE-0.3.0.md: -------------------------------------------------------------------------------- 1 | UPGRADE FROM 0.1.0 and 0.2.0 to 0.3.0 2 | ===================================== 3 | 4 | Now Peanut supports deploying certain versions. it is not required when you call the API since it will use the default (latest version). You will need to do the following in order to update 5 | 6 | * Update config file `containerization` property. 7 | 8 | ```yaml 9 | # Containerization runtime (supported docker) 10 | containerization: 11 | driver: ${PEANUT_CONTAINERIZATION_DRIVER:-docker} 12 | 13 | # Clean up stale images, volumes and networks 14 | autoClean: ${PEANUT_CONTAINERIZATION_AUTO_CLEAN:-true} 15 | 16 | # Time to cache docker images tags 17 | cacheTagsTimeInMinutes: ${PEANUT_CONTAINERIZATION_CACHE_TIME:-10080} 18 | ``` 19 | -------------------------------------------------------------------------------- /bin/docker-compose: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # cp ./bin/docker-compose /usr/local/bin/ 4 | if [[ "$*" == *port* ]] 5 | then 6 | echo "0.0.0.0:49156" 7 | else 8 | echo "Args: "$@ 9 | 10 | sleep 1 11 | echo "Start .." 12 | sleep 1 13 | 14 | for i in 1 2 3 4 5 6 7 8 9 10 15 | do 16 | echo "Task $i .." 17 | sleep 1 18 | done 19 | 20 | echo "Hold on .." 21 | sleep 5 22 | echo "End .." 23 | 24 | sleep 1 25 | echo "Done!" 26 | fi 27 | -------------------------------------------------------------------------------- /bin/install_etcd_ubuntu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ETCD_VER=v3.4.14 4 | 5 | # choose either URL 6 | GOOGLE_URL=https://storage.googleapis.com/etcd 7 | GITHUB_URL=https://github.com/etcd-io/etcd/releases/download 8 | DOWNLOAD_URL=${GOOGLE_URL} 9 | 10 | rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz 11 | rm -rf /tmp/etcd-download-test && mkdir -p /tmp/etcd-download-test 12 | 13 | curl -L ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz -o /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz 14 | tar xzvf /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz -C /tmp/etcd-download-test --strip-components=1 15 | rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz 16 | 17 | /tmp/etcd-download-test/etcd --version 18 | /tmp/etcd-download-test/etcdctl version 19 | 20 | cp /tmp/etcd-download-test/etcd /usr/local/bin/ 21 | cp /tmp/etcd-download-test/etcdctl /usr/local/bin/ 22 | 23 | mkdir -p /var/lib/etcd/ 24 | mkdir /etc/etcd 25 | 26 | groupadd --system etcd 27 | useradd -s /sbin/nologin --system -g etcd etcd 28 | 29 | chown -R etcd:etcd /var/lib/etcd/ 30 | 31 | echo "[Unit] 32 | Description=Etcd KV Store 33 | Documentation=https://github.com/etcd-io/etcd 34 | After=network.target 35 | 36 | [Service] 37 | User=etcd 38 | Type=notify 39 | Environment=ETCD_DATA_DIR=/var/lib/etcd 40 | Environment=ETCD_NAME=%m 41 | ExecStart=/usr/local/bin/etcd --listen-client-urls http://0.0.0.0:2379 --advertise-client-urls http://0.0.0.0:2379 42 | Restart=always 43 | RestartSec=10s 44 | LimitNOFILE=40000 45 | 46 | [Install] 47 | WantedBy=multi-user.target" > /etc/systemd/system/etcd.service 48 | 49 | systemctl daemon-reload 50 | systemctl start etcd.service 51 | 52 | # Then enable authentication on etcd server 53 | # etcdctl user add root 54 | # etcdctl auth enable 55 | -------------------------------------------------------------------------------- /bin/install_rabbitmq_ubuntu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | apt-get update 4 | echo "deb http://www.rabbitmq.com/debian/ testing main" >> /etc/apt/sources.list 5 | wget -O- https://www.rabbitmq.com/rabbitmq-release-signing-key.asc | sudo apt-key add - 6 | 7 | apt-get update 8 | sudo apt-get install -y rabbitmq-server 9 | 10 | sudo systemctl enable rabbitmq-server 11 | sudo systemctl start rabbitmq-server 12 | 13 | sudo rabbitmqctl add_user admin password 14 | sudo rabbitmqctl set_user_tags admin administrator 15 | sudo rabbitmqctl set_permissions -p / admin ".*" ".*" ".*" 16 | 17 | sudo rabbitmq-plugins enable rabbitmq_management 18 | 19 | # Login into http://127.0.0.1:15672/ 20 | # admin/password 21 | -------------------------------------------------------------------------------- /bin/local_etcd_mac.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd cache 4 | 5 | # Cleanup 6 | rm etcd-v3.4.14-darwin-amd64.zip 7 | rm -rf etcd-v3.4.14-darwin-amd64 8 | rm etcd.log 9 | rm -rf default.etcd 10 | 11 | curl -OL https://github.com/etcd-io/etcd/releases/download/v3.4.14/etcd-v3.4.14-darwin-amd64.zip 12 | unzip etcd-v3.4.14-darwin-amd64.zip 13 | 14 | ./etcd-v3.4.14-darwin-amd64/etcd > etcd.log 2>&1 & 15 | 16 | echo "===> etcd PID:" $! 17 | 18 | sleep 10 19 | 20 | curl -L http://127.0.0.1:2379/version 21 | 22 | cd .. 23 | -------------------------------------------------------------------------------- /bin/local_etcd_ubuntu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd cache 4 | 5 | # Cleanup 6 | rm etcd-v3.4.14-linux-amd64.tar.gz 7 | rm -rf etcd-v3.4.14-linux-amd64 8 | rm etcd.log 9 | rm -rf default.etcd 10 | 11 | curl -sL https://github.com/etcd-io/etcd/releases/download/v3.4.14/etcd-v3.4.14-linux-amd64.tar.gz | tar xz 12 | 13 | ./etcd-v3.4.14-linux-amd64/etcd > etcd.log 2>&1 & 14 | 15 | echo "===> etcd PID:" $! 16 | 17 | sleep 10 18 | 19 | curl -L http://127.0.0.1:2379/version 20 | 21 | cd .. 22 | -------------------------------------------------------------------------------- /bin/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Fetch latest version 4 | export LATEST_VERSION=$(curl --silent "https://api.github.com/repos/clivern/peanut/releases/latest" | jq '.tag_name' | sed -E 's/.*"([^"]+)".*/\1/') 5 | 6 | # Update go checksum database (sum.golang.org) immediately after release 7 | curl --silent https://sum.golang.org/lookup/github.com/clivern/peanut@{$LATEST_VERSION} 8 | -------------------------------------------------------------------------------- /bin/test_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PROGNAME="$(basename $0)" 4 | 5 | error_exit() 6 | { 7 | echo "${PROGNAME}: ${1:-"Unknown Error"}" 1>&2 8 | exit 1 9 | } 10 | 11 | echo "Hello World" 12 | echo "Example of error with line number and message" 13 | error_exit "$LINENO: An error has occurred." 14 | -------------------------------------------------------------------------------- /cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /cmd/configure.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | -------------------------------------------------------------------------------- /cmd/deploy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | -------------------------------------------------------------------------------- /cmd/destroy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | -------------------------------------------------------------------------------- /cmd/license.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | var licenseCmd = &cobra.Command{ 14 | Use: "license", 15 | Short: "Print the license", 16 | Run: func(cmd *cobra.Command, args []string) { 17 | fmt.Println(`MIT License 18 | 19 | Copyright (c) 2021 Clivern 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE.`) 38 | }, 39 | } 40 | 41 | func init() { 42 | rootCmd.AddCommand(licenseCmd) 43 | } 44 | -------------------------------------------------------------------------------- /cmd/list.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var config string 15 | 16 | var rootCmd = &cobra.Command{ 17 | Use: "peanut", 18 | Short: `🐺 Deploy Databases and Services Easily for Development and Testing Pipelines 19 | 20 | If you have any suggestions, bug reports, or annoyances please report 21 | them to our issue tracker at `, 22 | } 23 | 24 | // Execute runs cmd tool 25 | func Execute() { 26 | if err := rootCmd.Execute(); err != nil { 27 | fmt.Println(err) 28 | os.Exit(1) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cmd/version.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | 7 | import ( 8 | "embed" 9 | "fmt" 10 | 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var ( 15 | // Version buildinfo item 16 | Version = "dev" 17 | // Commit buildinfo item 18 | Commit = "none" 19 | // Date buildinfo item 20 | Date = "unknown" 21 | // BuiltBy buildinfo item 22 | BuiltBy = "unknown" 23 | ) 24 | 25 | var Static embed.FS 26 | 27 | var versionCmd = &cobra.Command{ 28 | Use: "version", 29 | Short: "Print the version number", 30 | Run: func(cmd *cobra.Command, args []string) { 31 | fmt.Println( 32 | fmt.Sprintf( 33 | `Current Peanut Version %v Commit %v, Built @%v By %v.`, 34 | Version, 35 | Commit, 36 | Date, 37 | BuiltBy, 38 | ), 39 | ) 40 | }, 41 | } 42 | 43 | func init() { 44 | rootCmd.AddCommand(versionCmd) 45 | } 46 | -------------------------------------------------------------------------------- /config.dist.yml: -------------------------------------------------------------------------------- 1 | # App configs 2 | app: 3 | # Env mode (dev or prod) 4 | mode: ${PEANUT_APP_MODE:-dev} 5 | # HTTP port 6 | port: ${PEANUT_API_PORT:-8000} 7 | # Hostname 8 | hostname: ${PEANUT_API_HOSTNAME:-127.0.0.1} 9 | # TLS configs 10 | tls: 11 | status: ${PEANUT_API_TLS_STATUS:-off} 12 | pemPath: ${PEANUT_API_TLS_PEMPATH:-cert/server.pem} 13 | keyPath: ${PEANUT_API_TLS_KEYPATH:-cert/server.key} 14 | 15 | # Containerization runtime (supported docker) 16 | containerization: 17 | driver: ${PEANUT_CONTAINERIZATION_DRIVER:-docker} 18 | 19 | # Clean up stale images, volumes and networks 20 | autoClean: ${PEANUT_CONTAINERIZATION_AUTO_CLEAN:-true} 21 | 22 | # Time to cache docker images tags 23 | cacheTagsTimeInMinutes: ${PEANUT_CONTAINERIZATION_CACHE_TIME:-10080} 24 | 25 | # App Storage 26 | storage: 27 | # Type (only local supported) 28 | type: ${PEANUT_STORAGE_TYPE:-local} 29 | # Local Path 30 | path: ${PEANUT_STORAGE_PATH:-/tmp} 31 | 32 | # API Configs 33 | api: 34 | key: ${PEANUT_API_KEY:-6c68b836-6f8e-465e-b59f-89c1db53afca} 35 | 36 | # Async Workers 37 | workers: 38 | # Queue max capacity 39 | buffer: ${PEANUT_WORKERS_CHAN_CAPACITY:-5000} 40 | # Number of concurrent workers 41 | count: ${PEANUT_WORKERS_COUNT:-4} 42 | 43 | # Runtime, Requests/Response and Peanut Metrics 44 | metrics: 45 | prometheus: 46 | # Route for the metrics endpoint 47 | endpoint: ${PEANUT_METRICS_PROM_ENDPOINT:-/metrics} 48 | 49 | # Application Database 50 | database: 51 | # Database driver 52 | driver: ${PEANUT_DB_DRIVER:-etcd} 53 | 54 | # Etcd Configs 55 | etcd: 56 | # Etcd database name or prefix 57 | databaseName: ${PEANUT_DB_ETCD_DB:-peanut} 58 | # Etcd username 59 | username: ${PEANUT_DB_ETCD_USERNAME:- } 60 | # Etcd password 61 | password: ${PEANUT_DB_ETCD_PASSWORD:- } 62 | # Etcd endpoints 63 | endpoints: ${PEANUT_DB_ETCD_ENDPOINTS:-http://127.0.0.1:2379} 64 | # Timeout in seconds 65 | timeout: 30 66 | 67 | # Log configs 68 | log: 69 | # Log level, it can be debug, info, warn, error, panic, fatal 70 | level: ${PEANUT_LOG_LEVEL:-info} 71 | # Output can be stdout or abs path to log file /var/logs/peanut.log 72 | output: ${PEANUT_LOG_OUTPUT:-stdout} 73 | # Format can be json 74 | format: ${PEANUT_LOG_FORMAT:-json} 75 | -------------------------------------------------------------------------------- /config.prod.yml: -------------------------------------------------------------------------------- 1 | # App configs 2 | app: 3 | # Env mode (dev or prod) 4 | mode: ${PEANUT_APP_MODE:-prod} 5 | # HTTP port 6 | port: ${PEANUT_API_PORT:-80} 7 | # Hostname 8 | hostname: ${PEANUT_API_HOSTNAME:-127.0.0.1} 9 | # TLS configs 10 | tls: 11 | status: ${PEANUT_API_TLS_STATUS:-off} 12 | pemPath: ${PEANUT_API_TLS_PEMPATH:-cert/server.pem} 13 | keyPath: ${PEANUT_API_TLS_KEYPATH:-cert/server.key} 14 | 15 | # Containerization runtime (supported docker) 16 | containerization: 17 | driver: ${PEANUT_CONTAINERIZATION_DRIVER:-docker} 18 | 19 | # Clean up stale images, volumes and networks 20 | autoClean: ${PEANUT_CONTAINERIZATION_AUTO_CLEAN:-true} 21 | 22 | # Time to cache docker images tags 23 | cacheTagsTimeInMinutes: ${PEANUT_CONTAINERIZATION_CACHE_TIME:-10080} 24 | 25 | # App Storage 26 | storage: 27 | # Type (only local supported) 28 | type: ${PEANUT_STORAGE_TYPE:-local} 29 | # Local Path 30 | path: ${PEANUT_STORAGE_PATH:-/etc/peanut/storage} 31 | 32 | # API Configs 33 | api: 34 | key: ${PEANUT_API_KEY:-6c68b836-6f8e-465e-b59f-89c1db53afca} 35 | 36 | # Async Workers 37 | workers: 38 | # Queue max capacity 39 | buffer: ${PEANUT_WORKERS_CHAN_CAPACITY:-5000} 40 | # Number of concurrent workers 41 | count: ${PEANUT_WORKERS_COUNT:-4} 42 | 43 | # Runtime, Requests/Response and Peanut Metrics 44 | metrics: 45 | prometheus: 46 | # Route for the metrics endpoint 47 | endpoint: ${PEANUT_METRICS_PROM_ENDPOINT:-/metrics} 48 | 49 | # Application Database 50 | database: 51 | # Database driver 52 | driver: ${PEANUT_DB_DRIVER:-etcd} 53 | 54 | # Etcd Configs 55 | etcd: 56 | # Etcd database name or prefix 57 | databaseName: ${PEANUT_DB_ETCD_DB:-peanut} 58 | # Etcd username 59 | username: ${PEANUT_DB_ETCD_USERNAME:- } 60 | # Etcd password 61 | password: ${PEANUT_DB_ETCD_PASSWORD:- } 62 | # Etcd endpoints 63 | endpoints: ${PEANUT_DB_ETCD_ENDPOINTS:-http://127.0.0.1:2379} 64 | # Timeout in seconds 65 | timeout: 30 66 | 67 | # Log configs 68 | log: 69 | # Log level, it can be debug, info, warn, error, panic, fatal 70 | level: ${PEANUT_LOG_LEVEL:-info} 71 | # Output can be stdout or abs path to log file /var/logs/peanut.log 72 | output: ${PEANUT_LOG_OUTPUT:-/var/logs/peanut.log} 73 | # Format can be json 74 | format: ${PEANUT_LOG_FORMAT:-json} 75 | -------------------------------------------------------------------------------- /config.toml: -------------------------------------------------------------------------------- 1 | ignoreGeneratedHeader = false 2 | severity = "warning" 3 | confidence = 0.8 4 | errorCode = 0 5 | warningCode = 0 6 | 7 | [rule.blank-imports] 8 | [rule.context-as-argument] 9 | [rule.context-keys-type] 10 | [rule.dot-imports] 11 | [rule.error-return] 12 | [rule.error-strings] 13 | [rule.error-naming] 14 | [rule.exported] 15 | [rule.if-return] 16 | [rule.increment-decrement] 17 | [rule.var-naming] 18 | [rule.var-declaration] 19 | [rule.package-comments] 20 | [rule.range] 21 | [rule.receiver-naming] 22 | [rule.time-naming] 23 | [rule.unexported-return] 24 | [rule.indent-error-flow] 25 | [rule.errorf] 26 | [rule.empty-block] 27 | [rule.superfluous-else] 28 | [rule.unused-parameter] 29 | [rule.unreachable-code] 30 | [rule.redefines-builtin-id] -------------------------------------------------------------------------------- /core/controller/health.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package controller 6 | 7 | import ( 8 | "net/http" 9 | 10 | "github.com/gin-gonic/gin" 11 | log "github.com/sirupsen/logrus" 12 | ) 13 | 14 | // Health controller 15 | func Health(c *gin.Context) { 16 | 17 | log.WithFields(log.Fields{ 18 | "correlation_id": c.Request.Header.Get("X-Correlation-ID"), 19 | "status": "ok", 20 | }).Info(`Health check`) 21 | 22 | c.JSON(http.StatusOK, gin.H{ 23 | "status": "ok", 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /core/controller/job.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package controller 6 | 7 | import ( 8 | "fmt" 9 | "net/http" 10 | "strings" 11 | "time" 12 | 13 | "github.com/clivern/peanut/core/driver" 14 | "github.com/clivern/peanut/core/model" 15 | 16 | "github.com/gin-gonic/gin" 17 | log "github.com/sirupsen/logrus" 18 | ) 19 | 20 | // GetJob controller 21 | func GetJob(c *gin.Context) { 22 | jobID := c.Param("jobId") 23 | serviceID := c.Param("serviceId") 24 | 25 | db := driver.NewEtcdDriver() 26 | 27 | err := db.Connect() 28 | 29 | if err != nil { 30 | log.WithFields(log.Fields{ 31 | "correlation_id": c.GetHeader("x-correlation-id"), 32 | "error": err.Error(), 33 | }).Error("Internal server error") 34 | 35 | c.JSON(http.StatusInternalServerError, gin.H{ 36 | "correlationID": c.GetHeader("x-correlation-id"), 37 | "errorMessage": "Internal server error", 38 | }) 39 | return 40 | } 41 | 42 | defer db.Close() 43 | 44 | jobStore := model.NewJobStore(db) 45 | 46 | jobData, err := jobStore.GetRecord(serviceID, jobID) 47 | 48 | if err != nil && strings.Contains(err.Error(), "Unable to find") { 49 | c.JSON(http.StatusNotFound, gin.H{ 50 | "correlationID": c.GetHeader("x-correlation-id"), 51 | "errorMessage": fmt.Sprintf("Unable to find job: %s", jobID), 52 | }) 53 | return 54 | } 55 | 56 | if err != nil { 57 | log.WithFields(log.Fields{ 58 | "correlation_id": c.GetHeader("x-correlation-id"), 59 | "error": err.Error(), 60 | }).Error("Internal server error") 61 | 62 | c.JSON(http.StatusInternalServerError, gin.H{ 63 | "correlationID": c.GetHeader("x-correlation-id"), 64 | "errorMessage": "Internal server error", 65 | }) 66 | return 67 | } 68 | 69 | c.JSON(http.StatusOK, gin.H{ 70 | "id": jobData.ID, 71 | "action": jobData.Action, 72 | "status": jobData.Status, 73 | "createdAt": time.Unix(jobData.CreatedAt, 0), 74 | "updatedAt": time.Unix(jobData.UpdatedAt, 0), 75 | }) 76 | } 77 | -------------------------------------------------------------------------------- /core/controller/metrics.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package controller 6 | 7 | import ( 8 | "net/http" 9 | 10 | "github.com/prometheus/client_golang/prometheus" 11 | "github.com/prometheus/client_golang/prometheus/promhttp" 12 | "github.com/spf13/viper" 13 | ) 14 | 15 | var ( 16 | workersCount = prometheus.NewGauge( 17 | prometheus.GaugeOpts{ 18 | Namespace: "peanut", 19 | Name: "workers_count", 20 | Help: "Number of Async Workers", 21 | }) 22 | 23 | queueCapacity = prometheus.NewGauge( 24 | prometheus.GaugeOpts{ 25 | Namespace: "peanut", 26 | Name: "workers_queue_capacity", 27 | Help: "The maximum number of messages queue can process", 28 | }) 29 | ) 30 | 31 | func init() { 32 | prometheus.MustRegister(workersCount) 33 | prometheus.MustRegister(queueCapacity) 34 | } 35 | 36 | // Metrics controller 37 | func Metrics() http.Handler { 38 | workersCount.Set(float64(viper.GetInt("app.broker.native.workers"))) 39 | queueCapacity.Set(float64(viper.GetInt("app.broker.native.capacity"))) 40 | 41 | return promhttp.Handler() 42 | } 43 | -------------------------------------------------------------------------------- /core/controller/ready.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package controller 6 | 7 | import ( 8 | "net/http" 9 | 10 | "github.com/clivern/peanut/core/driver" 11 | 12 | "github.com/gin-gonic/gin" 13 | log "github.com/sirupsen/logrus" 14 | ) 15 | 16 | // Ready controller 17 | func Ready(c *gin.Context) { 18 | db := driver.NewEtcdDriver() 19 | 20 | err := db.Connect() 21 | 22 | if err != nil || !db.IsConnected() { 23 | log.WithFields(log.Fields{ 24 | "correlation_id": c.Request.Header.Get("X-Correlation-ID"), 25 | "status": "NotOk", 26 | }).Info(`Ready check`) 27 | 28 | c.JSON(http.StatusInternalServerError, gin.H{ 29 | "status": "NotOk", 30 | }) 31 | 32 | return 33 | } 34 | 35 | defer db.Close() 36 | 37 | log.WithFields(log.Fields{ 38 | "correlation_id": c.Request.Header.Get("X-Correlation-ID"), 39 | "status": "ok", 40 | }).Info(`Ready check`) 41 | 42 | c.JSON(http.StatusOK, gin.H{ 43 | "status": "ok", 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /core/controller/service.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package controller 6 | 7 | import ( 8 | "fmt" 9 | "net/http" 10 | "strings" 11 | "time" 12 | 13 | "github.com/clivern/peanut/core/driver" 14 | "github.com/clivern/peanut/core/model" 15 | 16 | "github.com/gin-gonic/gin" 17 | log "github.com/sirupsen/logrus" 18 | "github.com/spf13/viper" 19 | ) 20 | 21 | // ServicePayload type 22 | type ServicePayload struct { 23 | ID string `json:"id"` 24 | Service string `json:"service"` 25 | Configs map[string]string `json:"configs"` 26 | DeleteAfter string `json:"deleteAfter"` 27 | Version string `json:"version"` 28 | CreatedAt time.Time `json:"createdAt"` 29 | UpdatedAt time.Time `json:"updatedAt"` 30 | } 31 | 32 | // GetServices controller 33 | func GetServices(c *gin.Context) { 34 | db := driver.NewEtcdDriver() 35 | 36 | err := db.Connect() 37 | 38 | if err != nil { 39 | log.WithFields(log.Fields{ 40 | "correlation_id": c.GetHeader("x-correlation-id"), 41 | "error": err.Error(), 42 | }).Error("Internal server error") 43 | 44 | c.JSON(http.StatusInternalServerError, gin.H{ 45 | "correlationID": c.GetHeader("x-correlation-id"), 46 | "errorMessage": "Internal server error", 47 | }) 48 | return 49 | } 50 | 51 | defer db.Close() 52 | 53 | serviceStore := model.NewServiceStore(db) 54 | 55 | data, err := serviceStore.GetRecords() 56 | 57 | if err != nil { 58 | log.WithFields(log.Fields{ 59 | "correlation_id": c.GetHeader("x-correlation-id"), 60 | "error": err.Error(), 61 | }).Error("Internal server error") 62 | 63 | c.JSON(http.StatusInternalServerError, gin.H{ 64 | "correlationID": c.GetHeader("x-correlation-id"), 65 | "errorMessage": "Internal server error", 66 | }) 67 | return 68 | } 69 | 70 | var services []ServicePayload 71 | 72 | for _, v := range data { 73 | v.Configs["address"] = viper.GetString("app.hostname") 74 | 75 | services = append(services, ServicePayload{ 76 | ID: v.ID, 77 | Service: v.Service, 78 | Configs: v.Configs, 79 | DeleteAfter: v.DeleteAfter, 80 | Version: v.Version, 81 | CreatedAt: time.Unix(v.CreatedAt, 0), 82 | UpdatedAt: time.Unix(v.UpdatedAt, 0), 83 | }) 84 | } 85 | 86 | c.JSON(http.StatusOK, gin.H{ 87 | "services": services, 88 | }) 89 | } 90 | 91 | // GetService controller 92 | func GetService(c *gin.Context) { 93 | serviceID := c.Param("serviceId") 94 | 95 | db := driver.NewEtcdDriver() 96 | 97 | err := db.Connect() 98 | 99 | if err != nil { 100 | log.WithFields(log.Fields{ 101 | "correlation_id": c.GetHeader("x-correlation-id"), 102 | "error": err.Error(), 103 | }).Error("Internal server error") 104 | 105 | c.JSON(http.StatusInternalServerError, gin.H{ 106 | "correlationID": c.GetHeader("x-correlation-id"), 107 | "errorMessage": "Internal server error", 108 | }) 109 | return 110 | } 111 | 112 | defer db.Close() 113 | 114 | serviceStore := model.NewServiceStore(db) 115 | 116 | serviceData, err := serviceStore.GetRecord(serviceID) 117 | 118 | if err != nil && strings.Contains(err.Error(), "Unable to find") { 119 | c.JSON(http.StatusNotFound, gin.H{ 120 | "correlationID": c.GetHeader("x-correlation-id"), 121 | "errorMessage": fmt.Sprintf("Unable to find job: %s", serviceID), 122 | }) 123 | return 124 | } 125 | 126 | if err != nil { 127 | log.WithFields(log.Fields{ 128 | "correlation_id": c.GetHeader("x-correlation-id"), 129 | "error": err.Error(), 130 | }).Error("Internal server error") 131 | 132 | c.JSON(http.StatusInternalServerError, gin.H{ 133 | "correlationID": c.GetHeader("x-correlation-id"), 134 | "errorMessage": "Internal server error", 135 | }) 136 | return 137 | } 138 | 139 | serviceData.Configs["address"] = viper.GetString("app.hostname") 140 | 141 | c.JSON(http.StatusOK, gin.H{ 142 | "id": serviceData.ID, 143 | "service": serviceData.Service, 144 | "configs": serviceData.Configs, 145 | "deleteAfter": serviceData.DeleteAfter, 146 | "version": serviceData.Version, 147 | "createdAt": time.Unix(serviceData.CreatedAt, 0), 148 | "updatedAt": time.Unix(serviceData.UpdatedAt, 0), 149 | }) 150 | } 151 | -------------------------------------------------------------------------------- /core/definition/base.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "strings" 9 | 10 | "gopkg.in/yaml.v2" 11 | ) 12 | 13 | // DockerComposeConfig type 14 | type DockerComposeConfig struct { 15 | Version string `yaml:"version"` 16 | Services map[string]Service `yaml:"services"` 17 | Networks map[string]string `yaml:"networks,omitempty"` 18 | Volumes map[string]string `yaml:"volumes,omitempty"` 19 | } 20 | 21 | // Service type 22 | type Service struct { 23 | Image string `yaml:"image"` 24 | Volumes []string `yaml:"volumes,omitempty"` 25 | Networks []string `yaml:"networks,omitempty"` 26 | Environment []string `yaml:"environment,omitempty"` 27 | DependsOn []string `yaml:"depends_on,omitempty"` 28 | Ports []string `yaml:"ports,omitempty"` 29 | Restart string `yaml:"restart,omitempty"` 30 | Command string `yaml:"command,omitempty"` 31 | } 32 | 33 | // ToString converts object to a string 34 | func (d *DockerComposeConfig) ToString() (string, error) { 35 | o, err := yaml.Marshal(&d) 36 | 37 | if err != nil { 38 | return "", err 39 | } 40 | 41 | result := strings.Replace(string(o), `omitempty`, "", -1) 42 | 43 | return result, nil 44 | } 45 | -------------------------------------------------------------------------------- /core/definition/cassandra.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // CassandraService const 13 | CassandraService = "cassandra" 14 | 15 | // CassandraPort const 16 | CassandraPort = "9042" 17 | 18 | // CassandraDockerImage const 19 | CassandraDockerImage = "cassandra" 20 | 21 | // CassandraDockerImageVersion const 22 | CassandraDockerImageVersion = "4.0" 23 | 24 | // CassandraRestartPolicy const 25 | CassandraRestartPolicy = "unless-stopped" 26 | ) 27 | 28 | // GetCassandraConfig gets yaml definition object 29 | func GetCassandraConfig(name, version string) DockerComposeConfig { 30 | services := make(map[string]Service) 31 | 32 | if version == "" { 33 | version = CassandraDockerImageVersion 34 | } 35 | 36 | services[name] = Service{ 37 | Image: fmt.Sprintf("%s:%s", CassandraDockerImage, version), 38 | Restart: CassandraRestartPolicy, 39 | Ports: []string{CassandraPort}, 40 | } 41 | 42 | return DockerComposeConfig{ 43 | Version: "3", 44 | Services: services, 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /core/definition/cassandra_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitCassandra test cases 16 | func TestUnitCassandra(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestCassandra", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | cassandra := GetCassandraConfig("cassandra", "") 22 | result, err := cassandra.ToString() 23 | 24 | g.Assert(strings.Contains( 25 | result, 26 | fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", CassandraDockerImage, CassandraDockerImageVersion)), 27 | )).Equal(true) 28 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, CassandraPort))).Equal(true) 29 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", CassandraRestartPolicy))).Equal(true) 30 | g.Assert(err).Equal(nil) 31 | }) 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /core/definition/clickhouse.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | -------------------------------------------------------------------------------- /core/definition/consul.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // ConsulService const 13 | ConsulService = "consul" 14 | 15 | // ConsulHTTPPort const 16 | ConsulHTTPPort = "8500" 17 | 18 | // ConsulDockerImage const 19 | ConsulDockerImage = "consul" 20 | 21 | // ConsulDockerImageVersion const 22 | ConsulDockerImageVersion = "1.9.7" 23 | 24 | // ConsulRestartPolicy const 25 | ConsulRestartPolicy = "unless-stopped" 26 | ) 27 | 28 | // GetConsulConfig gets yaml definition object 29 | func GetConsulConfig(name, version string) DockerComposeConfig { 30 | services := make(map[string]Service) 31 | 32 | if version == "" { 33 | version = ConsulDockerImageVersion 34 | } 35 | 36 | services[name] = Service{ 37 | Image: fmt.Sprintf("%s:%s", ConsulDockerImage, version), 38 | Restart: ConsulRestartPolicy, 39 | Ports: []string{ConsulHTTPPort}, 40 | Command: "consul agent -dev -client=0.0.0.0", 41 | } 42 | 43 | return DockerComposeConfig{ 44 | Version: "3", 45 | Services: services, 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/definition/consul_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitConsul test cases 16 | func TestUnitConsul(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestConsul", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | consul := GetConsulConfig("consul", "") 22 | result, err := consul.ToString() 23 | 24 | g.Assert(strings.Contains( 25 | result, 26 | fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", ConsulDockerImage, ConsulDockerImageVersion)), 27 | )).Equal(true) 28 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, ConsulHTTPPort))).Equal(true) 29 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", ConsulRestartPolicy))).Equal(true) 30 | g.Assert(err).Equal(nil) 31 | }) 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /core/definition/elasticsearch.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // ElasticSearchService const 13 | ElasticSearchService = "elasticsearch" 14 | 15 | // ElasticSearchRequestsPort const 16 | ElasticSearchRequestsPort = "9200" 17 | 18 | // ElasticSearchCommunicationPort const 19 | ElasticSearchCommunicationPort = "9300" 20 | 21 | // ElasticSearchDockerImage const 22 | ElasticSearchDockerImage = "elasticsearch" 23 | 24 | // ElasticSearchDockerImageVersion const 25 | ElasticSearchDockerImageVersion = "7.13.3" 26 | 27 | // ElasticSearchRestartPolicy const 28 | ElasticSearchRestartPolicy = "unless-stopped" 29 | ) 30 | 31 | // GetElasticSearchConfig gets yaml definition object 32 | func GetElasticSearchConfig(name, version string) DockerComposeConfig { 33 | services := make(map[string]Service) 34 | 35 | if version == "" { 36 | version = ElasticSearchDockerImageVersion 37 | } 38 | 39 | services[name] = Service{ 40 | Image: fmt.Sprintf("%s:%s", ElasticSearchDockerImage, version), 41 | Restart: ElasticSearchRestartPolicy, 42 | Ports: []string{ 43 | ElasticSearchRequestsPort, 44 | ElasticSearchCommunicationPort, 45 | }, 46 | Environment: []string{"discovery.type=single-node"}, 47 | } 48 | 49 | return DockerComposeConfig{ 50 | Version: "3", 51 | Services: services, 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /core/definition/elasticsearch_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitElasticSearch test cases 16 | func TestUnitElasticSearch(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestElasticSearch", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | elasticsearch := GetElasticSearchConfig("elasticsearch", "") 22 | result, err := elasticsearch.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", ElasticSearchDockerImage, ElasticSearchDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, ElasticSearchRequestsPort))).Equal(true) 26 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, ElasticSearchCommunicationPort))).Equal(true) 27 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", ElasticSearchRestartPolicy))).Equal(true) 28 | g.Assert(strings.Contains(result, "discovery.type=single-node")).Equal(true) 29 | g.Assert(err).Equal(nil) 30 | }) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /core/definition/etcd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // EtcdService const 13 | EtcdService = "etcd" 14 | 15 | // EtcdPort const 16 | EtcdPort = "2379" 17 | 18 | // EtcdDockerImage const 19 | EtcdDockerImage = "bitnami/etcd" 20 | 21 | // EtcdDockerImageVersion const 22 | EtcdDockerImageVersion = "3.5.0" 23 | 24 | // EtcdRestartPolicy const 25 | EtcdRestartPolicy = "unless-stopped" 26 | ) 27 | 28 | // GetEtcdConfig gets yaml definition object 29 | func GetEtcdConfig(name, version string) DockerComposeConfig { 30 | services := make(map[string]Service) 31 | 32 | if version == "" { 33 | version = EtcdDockerImageVersion 34 | } 35 | 36 | services[name] = Service{ 37 | Image: fmt.Sprintf("%s:%s", EtcdDockerImage, version), 38 | Restart: EtcdRestartPolicy, 39 | Ports: []string{EtcdPort}, 40 | Environment: []string{"ALLOW_NONE_AUTHENTICATION=yes"}, 41 | } 42 | 43 | return DockerComposeConfig{ 44 | Version: "3", 45 | Services: services, 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/definition/etcd_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitEtcd test cases 16 | func TestUnitEtcd(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestEtcd", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | etcd := GetEtcdConfig("etcd", "") 22 | result, err := etcd.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", EtcdDockerImage, EtcdDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, EtcdPort))).Equal(true) 26 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", EtcdRestartPolicy))).Equal(true) 27 | g.Assert(strings.Contains(result, "ALLOW_NONE_AUTHENTICATION=yes")).Equal(true) 28 | g.Assert(err).Equal(nil) 29 | }) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /core/definition/etherpad.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // EtherpadService const 13 | EtherpadService = "etherpad" 14 | 15 | // EtherpadPort const 16 | EtherpadPort = "9001" 17 | 18 | // EtherpadDockerImage const 19 | EtherpadDockerImage = "etherpad/etherpad" 20 | 21 | // EtherpadDockerImageVersion const 22 | EtherpadDockerImageVersion = "1.8.14" 23 | 24 | // EtherpadRestartPolicy const 25 | EtherpadRestartPolicy = "unless-stopped" 26 | ) 27 | 28 | // GetEtherpadConfig gets yaml definition object 29 | func GetEtherpadConfig(name, version string) DockerComposeConfig { 30 | services := make(map[string]Service) 31 | 32 | if version == "" { 33 | version = EtherpadDockerImageVersion 34 | } 35 | 36 | services[name] = Service{ 37 | Image: fmt.Sprintf("%s:%s", EtherpadDockerImage, version), 38 | Restart: EtherpadRestartPolicy, 39 | Ports: []string{EtherpadPort}, 40 | } 41 | 42 | return DockerComposeConfig{ 43 | Version: "3", 44 | Services: services, 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /core/definition/ghost.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // GhostService const 13 | GhostService = "ghost" 14 | 15 | // GhostPort const 16 | GhostPort = "2368" 17 | 18 | // GhostDockerImage const 19 | GhostDockerImage = "ghost" 20 | 21 | // GhostDockerImageVersion const 22 | GhostDockerImageVersion = "4.19.1" 23 | 24 | // GhostRestartPolicy const 25 | GhostRestartPolicy = "unless-stopped" 26 | ) 27 | 28 | // GetGhostConfig gets yaml definition object 29 | func GetGhostConfig(name, version string) DockerComposeConfig { 30 | services := make(map[string]Service) 31 | 32 | if version == "" { 33 | version = GhostDockerImageVersion 34 | } 35 | 36 | services[name] = Service{ 37 | Image: fmt.Sprintf("%s:%s", GhostDockerImage, version), 38 | Restart: GhostRestartPolicy, 39 | Ports: []string{GhostPort}, 40 | } 41 | 42 | return DockerComposeConfig{ 43 | Version: "3", 44 | Services: services, 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /core/definition/grafana.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // GrafanaService const 13 | GrafanaService = "grafana" 14 | 15 | // GrafanaPort const 16 | GrafanaPort = "3000" 17 | 18 | // GrafanaDockerImage const 19 | GrafanaDockerImage = "grafana/grafana" 20 | 21 | // GrafanaDockerImageVersion const 22 | GrafanaDockerImageVersion = "8.0.4" 23 | 24 | // GrafanaRestartPolicy const 25 | GrafanaRestartPolicy = "unless-stopped" 26 | 27 | // GrafanaDefaultUsername const 28 | GrafanaDefaultUsername = "admin" 29 | 30 | // GrafanaDefaultPassword const 31 | GrafanaDefaultPassword = "admin" 32 | 33 | // GrafanaDefaultAnonymousAccess const 34 | GrafanaDefaultAnonymousAccess = "true" 35 | 36 | // GrafanaDefaultAllowSignup const 37 | GrafanaDefaultAllowSignup = "false" 38 | ) 39 | 40 | // GetGrafanaConfig gets yaml definition object 41 | func GetGrafanaConfig(name, version, username, password, allowSignup, anonymousAccess string) DockerComposeConfig { 42 | services := make(map[string]Service) 43 | 44 | if username == "" { 45 | username = GrafanaDefaultUsername 46 | } 47 | 48 | if password == "" { 49 | password = GrafanaDefaultPassword 50 | } 51 | 52 | if allowSignup == "" { 53 | allowSignup = GrafanaDefaultAllowSignup 54 | } 55 | 56 | if anonymousAccess == "" { 57 | anonymousAccess = GrafanaDefaultAnonymousAccess 58 | } 59 | 60 | if version == "" { 61 | version = GrafanaDockerImageVersion 62 | } 63 | 64 | services[name] = Service{ 65 | Image: fmt.Sprintf("%s:%s", GrafanaDockerImage, version), 66 | Restart: GrafanaRestartPolicy, 67 | Ports: []string{GrafanaPort}, 68 | Environment: []string{ 69 | fmt.Sprintf("GF_SECURITY_ADMIN_USER=%s", username), 70 | fmt.Sprintf("GF_SECURITY_ADMIN_PASSWORD=%s", password), 71 | fmt.Sprintf("GF_USERS_ALLOW_SIGN_UP=%s", allowSignup), 72 | fmt.Sprintf("GF_AUTH_ANONYMOUS_ENABLED=%s", anonymousAccess), 73 | }, 74 | } 75 | 76 | return DockerComposeConfig{ 77 | Version: "3", 78 | Services: services, 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /core/definition/grafana_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitGrafana test cases 16 | func TestUnitGrafana(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestGrafana", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | grafana := GetGrafanaConfig("grafana", "", "grafana1", "grafana2", "true", "true") 22 | result, err := grafana.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", GrafanaDockerImage, GrafanaDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, GrafanaPort))).Equal(true) 26 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", GrafanaRestartPolicy))).Equal(true) 27 | 28 | g.Assert(strings.Contains(result, "GF_SECURITY_ADMIN_USER=grafana1")).Equal(true) 29 | g.Assert(strings.Contains(result, "GF_SECURITY_ADMIN_PASSWORD=grafana2")).Equal(true) 30 | g.Assert(strings.Contains(result, "GF_USERS_ALLOW_SIGN_UP=true")).Equal(true) 31 | g.Assert(strings.Contains(result, "GF_AUTH_ANONYMOUS_ENABLED=true")).Equal(true) 32 | g.Assert(err).Equal(nil) 33 | 34 | grafana = GetGrafanaConfig("grafana", "", "", "", "", "") 35 | result, err = grafana.ToString() 36 | 37 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", GrafanaDockerImage, GrafanaDockerImageVersion)))).Equal(true) 38 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, GrafanaPort))).Equal(true) 39 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", GrafanaRestartPolicy))).Equal(true) 40 | 41 | g.Assert(strings.Contains(result, fmt.Sprintf("GF_SECURITY_ADMIN_USER=%s", GrafanaDefaultUsername))).Equal(true) 42 | g.Assert(strings.Contains(result, fmt.Sprintf("GF_SECURITY_ADMIN_PASSWORD=%s", GrafanaDefaultPassword))).Equal(true) 43 | g.Assert(strings.Contains(result, fmt.Sprintf("GF_AUTH_ANONYMOUS_ENABLED=%s", GrafanaDefaultAnonymousAccess))).Equal(true) 44 | g.Assert(strings.Contains(result, fmt.Sprintf("GF_USERS_ALLOW_SIGN_UP=%s", GrafanaDefaultAllowSignup))).Equal(true) 45 | g.Assert(err).Equal(nil) 46 | }) 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /core/definition/graphite.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // GraphiteService const 13 | GraphiteService = "graphite" 14 | 15 | // GraphiteWebPort const 16 | GraphiteWebPort = "80" 17 | 18 | // GraphiteCarbonPort const 19 | GraphiteCarbonPort = "2003" 20 | 21 | // GraphiteCarbonPicklePort const 22 | GraphiteCarbonPicklePort = "2004" 23 | 24 | // GraphiteCarbonAggregatorPort const 25 | GraphiteCarbonAggregatorPort = "2023" 26 | 27 | // GraphiteCarbonAggregatorPicklePort const 28 | GraphiteCarbonAggregatorPicklePort = "2024" 29 | 30 | // GraphiteStatsdPort const 31 | GraphiteStatsdPort = "8125" 32 | 33 | // GraphiteStatsdAdminPort const 34 | GraphiteStatsdAdminPort = "8126" 35 | 36 | // GraphiteDockerImage const 37 | GraphiteDockerImage = "graphiteapp/graphite-statsd" 38 | 39 | // GraphiteDockerImageVersion const 40 | GraphiteDockerImageVersion = "1.1.7-11" 41 | 42 | // GraphiteRestartPolicy const 43 | GraphiteRestartPolicy = "unless-stopped" 44 | ) 45 | 46 | // GetGraphiteConfig gets yaml definition object 47 | func GetGraphiteConfig(name, version string) DockerComposeConfig { 48 | services := make(map[string]Service) 49 | 50 | if version == "" { 51 | version = GraphiteDockerImageVersion 52 | } 53 | 54 | services[name] = Service{ 55 | Image: fmt.Sprintf("%s:%s", GraphiteDockerImage, version), 56 | Restart: GraphiteRestartPolicy, 57 | Ports: []string{ 58 | GraphiteWebPort, 59 | fmt.Sprintf("%s-%s", GraphiteCarbonPort, GraphiteCarbonPicklePort), 60 | fmt.Sprintf("%s-%s", GraphiteCarbonAggregatorPort, GraphiteCarbonAggregatorPicklePort), 61 | GraphiteStatsdPort, 62 | GraphiteStatsdAdminPort, 63 | }, 64 | } 65 | 66 | return DockerComposeConfig{ 67 | Version: "3", 68 | Services: services, 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /core/definition/graphite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitGraphite test cases 16 | func TestUnitGraphite(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestGraphite", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | graphite := GetGraphiteConfig("graphite", "") 22 | result, err := graphite.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", GraphiteDockerImage, GraphiteDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, GraphiteWebPort))).Equal(true) 26 | g.Assert(strings.Contains(result, fmt.Sprintf(`- %s-%s`, GraphiteCarbonPort, GraphiteCarbonPicklePort))).Equal(true) 27 | g.Assert(strings.Contains(result, fmt.Sprintf(`- %s-%s`, GraphiteCarbonAggregatorPort, GraphiteCarbonAggregatorPicklePort))).Equal(true) 28 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, GraphiteStatsdPort))).Equal(true) 29 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, GraphiteStatsdAdminPort))).Equal(true) 30 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", GraphiteRestartPolicy))).Equal(true) 31 | g.Assert(err).Equal(nil) 32 | }) 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /core/definition/httpbin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // HttpbinService const 13 | HttpbinService = "httpbin" 14 | 15 | // HttpbinPort const 16 | HttpbinPort = "80" 17 | 18 | // HttpbinDockerImage const 19 | HttpbinDockerImage = "kennethreitz/httpbin" 20 | 21 | // HttpbinDockerImageVersion const 22 | HttpbinDockerImageVersion = "latest" 23 | 24 | // HttpbinRestartPolicy const 25 | HttpbinRestartPolicy = "unless-stopped" 26 | ) 27 | 28 | // GetHttpbinConfig gets yaml definition object 29 | func GetHttpbinConfig(name, version string) DockerComposeConfig { 30 | services := make(map[string]Service) 31 | 32 | if version == "" { 33 | version = HttpbinDockerImageVersion 34 | } 35 | 36 | services[name] = Service{ 37 | Image: fmt.Sprintf("%s:%s", HttpbinDockerImage, version), 38 | Restart: HttpbinRestartPolicy, 39 | Ports: []string{HttpbinPort}, 40 | } 41 | 42 | return DockerComposeConfig{ 43 | Version: "3", 44 | Services: services, 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /core/definition/httpbin_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitHttpBin test cases 16 | func TestUnitHttpBin(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestHttpBin", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | httpbin := GetHttpbinConfig("httpbin", "") 22 | result, err := httpbin.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", HttpbinDockerImage, HttpbinDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, HttpbinPort))).Equal(true) 26 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", HttpbinRestartPolicy))).Equal(true) 27 | g.Assert(err).Equal(nil) 28 | }) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /core/definition/jaeger.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // JaegerService const 13 | JaegerService = "jaeger" 14 | 15 | // JaegerUDPPort1 const 16 | JaegerUDPPort1 = "5775" 17 | 18 | // JaegerUDPPort2 const 19 | JaegerUDPPort2 = "6831" 20 | 21 | // JaegerUDPPort3 const 22 | JaegerUDPPort3 = "6832" 23 | 24 | // JaegerHTTPPort1 const 25 | JaegerHTTPPort1 = "5778" 26 | 27 | // JaegerHTTPPort2 const 28 | JaegerHTTPPort2 = "16686" 29 | 30 | // JaegerHTTPPort3 const 31 | JaegerHTTPPort3 = "14268" 32 | 33 | // JaegerHTTPPort4 const 34 | JaegerHTTPPort4 = "14250" 35 | 36 | // JaegerHTTPPort5 const 37 | JaegerHTTPPort5 = "9411" 38 | 39 | // JaegerDockerImage const 40 | JaegerDockerImage = "jaegertracing/all-in-one" 41 | 42 | // JaegerDockerImageVersion const 43 | JaegerDockerImageVersion = "1.24" 44 | 45 | // JaegerRestartPolicy const 46 | JaegerRestartPolicy = "unless-stopped" 47 | ) 48 | 49 | // GetJaegerConfig gets yaml definition object 50 | func GetJaegerConfig(name, version string) DockerComposeConfig { 51 | services := make(map[string]Service) 52 | 53 | if version == "" { 54 | version = JaegerDockerImageVersion 55 | } 56 | 57 | services[name] = Service{ 58 | Image: fmt.Sprintf("%s:%s", JaegerDockerImage, version), 59 | Restart: JaegerRestartPolicy, 60 | Ports: []string{ 61 | JaegerUDPPort1, 62 | JaegerUDPPort2, 63 | JaegerUDPPort3, 64 | JaegerHTTPPort1, 65 | JaegerHTTPPort2, 66 | JaegerHTTPPort3, 67 | JaegerHTTPPort4, 68 | JaegerHTTPPort5, 69 | }, 70 | } 71 | 72 | return DockerComposeConfig{ 73 | Version: "3", 74 | Services: services, 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /core/definition/jaeger_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitJaeger test cases 16 | func TestUnitJaeger(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestJaeger", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | jaeger := GetJaegerConfig("jaeger", "") 22 | result, err := jaeger.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", JaegerDockerImage, JaegerDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, JaegerUDPPort1))).Equal(true) 26 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, JaegerUDPPort2))).Equal(true) 27 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, JaegerUDPPort3))).Equal(true) 28 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, JaegerHTTPPort1))).Equal(true) 29 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, JaegerHTTPPort2))).Equal(true) 30 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, JaegerHTTPPort3))).Equal(true) 31 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, JaegerHTTPPort4))).Equal(true) 32 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, JaegerHTTPPort5))).Equal(true) 33 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", JaegerRestartPolicy))).Equal(true) 34 | g.Assert(err).Equal(nil) 35 | }) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /core/definition/jenkins.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | -------------------------------------------------------------------------------- /core/definition/kafka.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | -------------------------------------------------------------------------------- /core/definition/mailhog.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // MailhogService const 13 | MailhogService = "mailhog" 14 | 15 | // MailhogSMTPPort const 16 | MailhogSMTPPort = "1025" 17 | 18 | // MailhogHTTPPort const 19 | MailhogHTTPPort = "8025" 20 | 21 | // MailhogDockerImage const 22 | MailhogDockerImage = "mailhog/mailhog" 23 | 24 | // MailhogDockerImageVersion const 25 | MailhogDockerImageVersion = "v1.0.1" 26 | 27 | // MailhogRestartPolicy const 28 | MailhogRestartPolicy = "unless-stopped" 29 | ) 30 | 31 | // GetMailhogConfig gets yaml definition object 32 | func GetMailhogConfig(name, version string) DockerComposeConfig { 33 | services := make(map[string]Service) 34 | 35 | if version == "" { 36 | version = MailhogDockerImageVersion 37 | } 38 | 39 | services[name] = Service{ 40 | Image: fmt.Sprintf("%s:%s", MailhogDockerImage, version), 41 | Restart: MailhogRestartPolicy, 42 | Ports: []string{ 43 | MailhogSMTPPort, 44 | MailhogHTTPPort, 45 | }, 46 | } 47 | 48 | return DockerComposeConfig{ 49 | Version: "3", 50 | Services: services, 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /core/definition/mailhog_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitMailhog test cases 16 | func TestUnitMailhog(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestMailhog", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | mailhog := GetMailhogConfig("mailhog", "") 22 | result, err := mailhog.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", MailhogDockerImage, MailhogDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, MailhogSMTPPort))).Equal(true) 26 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, MailhogHTTPPort))).Equal(true) 27 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", MailhogRestartPolicy))).Equal(true) 28 | g.Assert(err).Equal(nil) 29 | }) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /core/definition/mariadb.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // MariaDBService const 13 | MariaDBService = "mariadb" 14 | 15 | // MariaDBPort const 16 | MariaDBPort = "3306" 17 | 18 | // MariaDBDockerImage const 19 | MariaDBDockerImage = "mariadb" 20 | 21 | // MariaDBDockerImageVersion const 22 | MariaDBDockerImageVersion = "10.6.2" 23 | 24 | // MariaDBRestartPolicy const 25 | MariaDBRestartPolicy = "unless-stopped" 26 | 27 | // MariaDBDefaultRootPassword const 28 | MariaDBDefaultRootPassword = "root" 29 | 30 | // MariaDBDefaultDatabase const 31 | MariaDBDefaultDatabase = "peanut" 32 | 33 | // MariaDBDefaultUsername const 34 | MariaDBDefaultUsername = "peanut" 35 | 36 | // MariaDBDefaultPassword const 37 | MariaDBDefaultPassword = "secret" 38 | ) 39 | 40 | // GetMariaDBConfig gets yaml definition object 41 | func GetMariaDBConfig(name, version, rootPassword, database, username, password string) DockerComposeConfig { 42 | services := make(map[string]Service) 43 | 44 | if rootPassword == "" { 45 | rootPassword = MariaDBDefaultRootPassword 46 | } 47 | 48 | if database == "" { 49 | database = MariaDBDefaultDatabase 50 | } 51 | 52 | if username == "" { 53 | username = MariaDBDefaultUsername 54 | } 55 | 56 | if password == "" { 57 | password = MariaDBDefaultPassword 58 | } 59 | 60 | if version == "" { 61 | version = MariaDBDockerImageVersion 62 | } 63 | 64 | services[name] = Service{ 65 | Image: fmt.Sprintf("%s:%s", MariaDBDockerImage, version), 66 | Restart: MariaDBRestartPolicy, 67 | Ports: []string{MariaDBPort}, 68 | Environment: []string{ 69 | fmt.Sprintf("MYSQL_ROOT_PASSWORD=%s", rootPassword), 70 | fmt.Sprintf("MYSQL_DATABASE=%s", database), 71 | fmt.Sprintf("MYSQL_USER=%s", username), 72 | fmt.Sprintf("MYSQL_PASSWORD=%s", password), 73 | "MYSQL_ALLOW_EMPTY_PASSWORD=no", 74 | }, 75 | } 76 | 77 | return DockerComposeConfig{ 78 | Version: "3", 79 | Services: services, 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /core/definition/mariadb_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitMariaDB test cases 16 | func TestUnitMariaDB(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestMariaDB", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | mariadb := GetMariaDBConfig("mariadb", "", "mariadb1", "mariadb2", "mariadb3", "mariadb4") 22 | result, err := mariadb.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", MariaDBDockerImage, MariaDBDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, MariaDBPort))).Equal(true) 26 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", MariaDBRestartPolicy))).Equal(true) 27 | g.Assert(strings.Contains(result, "MYSQL_ALLOW_EMPTY_PASSWORD=no")).Equal(true) 28 | g.Assert(strings.Contains(result, "MYSQL_ROOT_PASSWORD=mariadb1")).Equal(true) 29 | g.Assert(strings.Contains(result, "MYSQL_DATABASE=mariadb2")).Equal(true) 30 | g.Assert(strings.Contains(result, "MYSQL_USER=mariadb3")).Equal(true) 31 | g.Assert(strings.Contains(result, "MYSQL_PASSWORD=mariadb4")).Equal(true) 32 | g.Assert(err).Equal(nil) 33 | 34 | mariadb = GetMariaDBConfig("mariadb", "", "", "", "", "") 35 | result, err = mariadb.ToString() 36 | 37 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", MariaDBDockerImage, MariaDBDockerImageVersion)))).Equal(true) 38 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, MariaDBPort))).Equal(true) 39 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", MariaDBRestartPolicy))).Equal(true) 40 | g.Assert(strings.Contains(result, "MYSQL_ALLOW_EMPTY_PASSWORD=no")).Equal(true) 41 | g.Assert(strings.Contains(result, fmt.Sprintf("MYSQL_ROOT_PASSWORD=%s", MariaDBDefaultRootPassword))).Equal(true) 42 | g.Assert(strings.Contains(result, fmt.Sprintf("MYSQL_DATABASE=%s", MariaDBDefaultDatabase))).Equal(true) 43 | g.Assert(strings.Contains(result, fmt.Sprintf("MYSQL_USER=%s", MariaDBDefaultUsername))).Equal(true) 44 | g.Assert(strings.Contains(result, fmt.Sprintf("MYSQL_PASSWORD=%s", MariaDBDefaultPassword))).Equal(true) 45 | g.Assert(err).Equal(nil) 46 | }) 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /core/definition/memcached.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // MemcachedService const 13 | MemcachedService = "memcached" 14 | 15 | // MemcachedPort const 16 | MemcachedPort = "11211" 17 | 18 | // MemcachedDockerImage const 19 | MemcachedDockerImage = "memcached" 20 | 21 | // MemcachedDockerImageVersion const 22 | MemcachedDockerImageVersion = "1.6.9" 23 | 24 | // MemcachedRestartPolicy const 25 | MemcachedRestartPolicy = "unless-stopped" 26 | ) 27 | 28 | // GetMemcachedConfig gets yaml definition object 29 | func GetMemcachedConfig(name, version string) DockerComposeConfig { 30 | services := make(map[string]Service) 31 | 32 | if version == "" { 33 | version = MemcachedDockerImageVersion 34 | } 35 | 36 | services[name] = Service{ 37 | Image: fmt.Sprintf("%s:%s", MemcachedDockerImage, version), 38 | Restart: MemcachedRestartPolicy, 39 | Ports: []string{MemcachedPort}, 40 | } 41 | 42 | return DockerComposeConfig{ 43 | Version: "3", 44 | Services: services, 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /core/definition/memcached_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitMemcached test cases 16 | func TestUnitMemcached(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestMemcached", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | memcached := GetMemcachedConfig("memcached", "") 22 | result, err := memcached.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", MemcachedDockerImage, MemcachedDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, MemcachedPort))).Equal(true) 26 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", MemcachedRestartPolicy))).Equal(true) 27 | g.Assert(err).Equal(nil) 28 | }) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /core/definition/minio.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // MinioService const 13 | MinioService = "minio" 14 | 15 | // MinioAPIPort const 16 | MinioAPIPort = "9000" 17 | 18 | // MinioConsolePort const 19 | MinioConsolePort = "9001" 20 | 21 | // MinioDockerImage const 22 | MinioDockerImage = "minio/minio" 23 | 24 | // MinioDockerImageVersion const 25 | MinioDockerImageVersion = "RELEASE.2021-07-27T02-40-15Z" 26 | 27 | // MinioRestartPolicy const 28 | MinioRestartPolicy = "unless-stopped" 29 | 30 | // MinioRootUser const 31 | MinioRootUser = "admin12345678" 32 | 33 | // MinioRootPassword const 34 | MinioRootPassword = "admin12345678" 35 | ) 36 | 37 | // GetMinioConfig gets yaml definition object 38 | func GetMinioConfig(name, version, username, password string) DockerComposeConfig { 39 | services := make(map[string]Service) 40 | 41 | if version == "" { 42 | version = MinioDockerImageVersion 43 | } 44 | 45 | if username == "" { 46 | username = MinioRootUser 47 | } 48 | 49 | if password == "" { 50 | password = MinioRootPassword 51 | } 52 | 53 | services[name] = Service{ 54 | Image: fmt.Sprintf("%s:%s", MinioDockerImage, version), 55 | Command: "server /data --console-address :9001", 56 | Restart: MinioRestartPolicy, 57 | Ports: []string{ 58 | MinioAPIPort, 59 | MinioConsolePort, 60 | }, 61 | Environment: []string{ 62 | fmt.Sprintf("MINIO_ROOT_USER=%s", username), 63 | fmt.Sprintf("MINIO_ROOT_PASSWORD=%s", password), 64 | }, 65 | } 66 | 67 | return DockerComposeConfig{ 68 | Version: "3", 69 | Services: services, 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /core/definition/minio_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitMinio test cases 16 | func TestUnitMinio(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestMinio", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | minio := GetMinioConfig("minio", "", "", "") 22 | result, err := minio.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", MinioDockerImage, MinioDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, MinioAPIPort))).Equal(true) 26 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, MinioConsolePort))).Equal(true) 27 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", MinioRestartPolicy))).Equal(true) 28 | g.Assert(err).Equal(nil) 29 | }) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /core/definition/mongodb.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // MongoDBService const 13 | MongoDBService = "mongodb" 14 | 15 | // MongoDBPort const 16 | MongoDBPort = "27017" 17 | 18 | // MongoDBDockerImage const 19 | MongoDBDockerImage = "mongo" 20 | 21 | // MongoDBDockerImageVersion const 22 | MongoDBDockerImageVersion = "5.0.0-rc7" 23 | 24 | // MongoDBRestartPolicy const 25 | MongoDBRestartPolicy = "unless-stopped" 26 | 27 | // MongoDBDefaultDatabase const 28 | MongoDBDefaultDatabase = "peanut" 29 | 30 | // MongoDBDefaultUsername const 31 | MongoDBDefaultUsername = "peanut" 32 | 33 | // MongoDBDefaultPassword const 34 | MongoDBDefaultPassword = "secret" 35 | ) 36 | 37 | // GetMongoDBConfig gets yaml definition object 38 | func GetMongoDBConfig(name, version, database, username, password string) DockerComposeConfig { 39 | services := make(map[string]Service) 40 | 41 | if database == "" { 42 | database = MongoDBDefaultDatabase 43 | } 44 | 45 | if username == "" { 46 | username = MongoDBDefaultUsername 47 | } 48 | 49 | if password == "" { 50 | password = MongoDBDefaultPassword 51 | } 52 | 53 | if version == "" { 54 | version = MongoDBDockerImageVersion 55 | } 56 | 57 | services[name] = Service{ 58 | Image: fmt.Sprintf("%s:%s", MongoDBDockerImage, version), 59 | Restart: MongoDBRestartPolicy, 60 | Ports: []string{MongoDBPort}, 61 | Environment: []string{ 62 | fmt.Sprintf("MONGO_INITDB_DATABASE=%s", database), 63 | fmt.Sprintf("MONGO_INITDB_ROOT_USERNAME=%s", username), 64 | fmt.Sprintf("MONGO_INITDB_ROOT_PASSWORD=%s", password), 65 | }, 66 | } 67 | 68 | return DockerComposeConfig{ 69 | Version: "3", 70 | Services: services, 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /core/definition/mongodb_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitMongoDB test cases 16 | func TestUnitMongoDB(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestMongoDB", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | mongodb := GetMongoDBConfig("mongodb", "", "db", "user", "pass") 22 | result, err := mongodb.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", MongoDBDockerImage, MongoDBDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, MongoDBPort))).Equal(true) 26 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", MongoDBRestartPolicy))).Equal(true) 27 | g.Assert(strings.Contains(result, "MONGO_INITDB_DATABASE=db")).Equal(true) 28 | g.Assert(strings.Contains(result, "MONGO_INITDB_ROOT_USERNAME=user")).Equal(true) 29 | g.Assert(strings.Contains(result, "MONGO_INITDB_ROOT_PASSWORD=pass")).Equal(true) 30 | g.Assert(err).Equal(nil) 31 | 32 | mongodb = GetMongoDBConfig("mongodb", "", "", "", "") 33 | result, err = mongodb.ToString() 34 | 35 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", MongoDBDockerImage, MongoDBDockerImageVersion)))).Equal(true) 36 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, MongoDBPort))).Equal(true) 37 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", MongoDBRestartPolicy))).Equal(true) 38 | g.Assert(strings.Contains(result, fmt.Sprintf("MONGO_INITDB_DATABASE=%s", MongoDBDefaultDatabase))).Equal(true) 39 | g.Assert(strings.Contains(result, fmt.Sprintf("MONGO_INITDB_ROOT_USERNAME=%s", MongoDBDefaultUsername))).Equal(true) 40 | g.Assert(strings.Contains(result, fmt.Sprintf("MONGO_INITDB_ROOT_PASSWORD=%s", MongoDBDefaultPassword))).Equal(true) 41 | g.Assert(err).Equal(nil) 42 | }) 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /core/definition/mysql.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // MySQLService const 13 | MySQLService = "mysql" 14 | 15 | // MySQLPort const 16 | MySQLPort = "3306" 17 | 18 | // MySQLDockerImage const 19 | MySQLDockerImage = "mysql" 20 | 21 | // MySQLDockerImageVersion const 22 | MySQLDockerImageVersion = "8.0" 23 | 24 | // MySQLRestartPolicy const 25 | MySQLRestartPolicy = "unless-stopped" 26 | 27 | // MySQLDefaultRootPassword const 28 | MySQLDefaultRootPassword = "root" 29 | 30 | // MySQLDefaultDatabase const 31 | MySQLDefaultDatabase = "peanut" 32 | 33 | // MySQLDefaultUsername const 34 | MySQLDefaultUsername = "peanut" 35 | 36 | // MySQLDefaultPassword const 37 | MySQLDefaultPassword = "secret" 38 | ) 39 | 40 | // GetMySQLConfig gets yaml definition object 41 | func GetMySQLConfig(name, version, rootPassword, database, username, password string) DockerComposeConfig { 42 | services := make(map[string]Service) 43 | 44 | if rootPassword == "" { 45 | rootPassword = MySQLDefaultRootPassword 46 | } 47 | 48 | if database == "" { 49 | database = MySQLDefaultDatabase 50 | } 51 | 52 | if username == "" { 53 | username = MySQLDefaultUsername 54 | } 55 | 56 | if password == "" { 57 | password = MySQLDefaultPassword 58 | } 59 | 60 | if version == "" { 61 | version = MySQLDockerImageVersion 62 | } 63 | 64 | services[name] = Service{ 65 | Image: fmt.Sprintf("%s:%s", MySQLDockerImage, version), 66 | Restart: MySQLRestartPolicy, 67 | Ports: []string{MySQLPort}, 68 | Environment: []string{ 69 | fmt.Sprintf("MYSQL_ROOT_PASSWORD=%s", rootPassword), 70 | fmt.Sprintf("MYSQL_DATABASE=%s", database), 71 | fmt.Sprintf("MYSQL_USER=%s", username), 72 | fmt.Sprintf("MYSQL_PASSWORD=%s", password), 73 | "MYSQL_ALLOW_EMPTY_PASSWORD=no", 74 | }, 75 | } 76 | 77 | return DockerComposeConfig{ 78 | Version: "3", 79 | Services: services, 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /core/definition/mysql_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitMySQL test cases 16 | func TestUnitMySQL(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestMySQL", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | mysql := GetMySQLConfig("mysql", "", "mysql1", "mysql2", "mysql3", "mysql4") 22 | result, err := mysql.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", MySQLDockerImage, MySQLDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, MySQLPort))).Equal(true) 26 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", MySQLRestartPolicy))).Equal(true) 27 | g.Assert(strings.Contains(result, "MYSQL_ALLOW_EMPTY_PASSWORD=no")).Equal(true) 28 | g.Assert(strings.Contains(result, "MYSQL_ROOT_PASSWORD=mysql1")).Equal(true) 29 | g.Assert(strings.Contains(result, "MYSQL_DATABASE=mysql2")).Equal(true) 30 | g.Assert(strings.Contains(result, "MYSQL_USER=mysql3")).Equal(true) 31 | g.Assert(strings.Contains(result, "MYSQL_PASSWORD=mysql4")).Equal(true) 32 | g.Assert(err).Equal(nil) 33 | 34 | mysql = GetMySQLConfig("mysql", "", "", "", "", "") 35 | result, err = mysql.ToString() 36 | 37 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", MySQLDockerImage, MySQLDockerImageVersion)))).Equal(true) 38 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, MySQLPort))).Equal(true) 39 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", MySQLRestartPolicy))).Equal(true) 40 | g.Assert(strings.Contains(result, "MYSQL_ALLOW_EMPTY_PASSWORD=no")).Equal(true) 41 | g.Assert(strings.Contains(result, fmt.Sprintf("MYSQL_ROOT_PASSWORD=%s", MySQLDefaultRootPassword))).Equal(true) 42 | g.Assert(strings.Contains(result, fmt.Sprintf("MYSQL_DATABASE=%s", MySQLDefaultDatabase))).Equal(true) 43 | g.Assert(strings.Contains(result, fmt.Sprintf("MYSQL_USER=%s", MySQLDefaultUsername))).Equal(true) 44 | g.Assert(strings.Contains(result, fmt.Sprintf("MYSQL_PASSWORD=%s", MySQLDefaultPassword))).Equal(true) 45 | g.Assert(err).Equal(nil) 46 | }) 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /core/definition/nagios.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // NagiosService const 13 | NagiosService = "nagios" 14 | 15 | // NagiosPort const 16 | NagiosPort = "80" 17 | 18 | // NagiosDockerImage const 19 | NagiosDockerImage = "jasonrivers/nagios" 20 | 21 | // NagiosDockerImageVersion const 22 | NagiosDockerImageVersion = "latest" 23 | 24 | // NagiosRestartPolicy const 25 | NagiosRestartPolicy = "unless-stopped" 26 | 27 | // NagiosRootUser const 28 | NagiosRootUser = "nagiosadmin" 29 | 30 | // NagiosRootPassword const 31 | NagiosRootPassword = "nagios" 32 | ) 33 | 34 | // GetNagiosConfig gets yaml definition object 35 | func GetNagiosConfig(name, version string) DockerComposeConfig { 36 | services := make(map[string]Service) 37 | 38 | if version == "" { 39 | version = NagiosDockerImageVersion 40 | } 41 | 42 | services[name] = Service{ 43 | Image: fmt.Sprintf("%s:%s", NagiosDockerImage, version), 44 | Restart: NagiosRestartPolicy, 45 | Ports: []string{NagiosPort}, 46 | } 47 | 48 | return DockerComposeConfig{ 49 | Version: "3", 50 | Services: services, 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /core/definition/nagios_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitNagios test cases 16 | func TestUnitNagios(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestNagios", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | httpbin := GetNagiosConfig("httpbin", "") 22 | result, err := httpbin.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", NagiosDockerImage, NagiosDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, NagiosPort))).Equal(true) 26 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", NagiosRestartPolicy))).Equal(true) 27 | g.Assert(err).Equal(nil) 28 | }) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /core/definition/postgresql.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // PostgreSQLService const 13 | PostgreSQLService = "postgresql" 14 | 15 | // PostgreSQLPort const 16 | PostgreSQLPort = "5432" 17 | 18 | // PostgreSQLDockerImage const 19 | PostgreSQLDockerImage = "postgres" 20 | 21 | // PostgreSQLDockerImageVersion const 22 | PostgreSQLDockerImageVersion = "13.3" 23 | 24 | // PostgreSQLRestartPolicy const 25 | PostgreSQLRestartPolicy = "unless-stopped" 26 | 27 | // PostgreSQLDefaultDatabase const 28 | PostgreSQLDefaultDatabase = "peanut" 29 | 30 | // PostgreSQLDefaultUsername const 31 | PostgreSQLDefaultUsername = "peanut" 32 | 33 | // PostgreSQLDefaultPassword const 34 | PostgreSQLDefaultPassword = "secret" 35 | ) 36 | 37 | // GetPostgreSQLConfig gets yaml definition object 38 | func GetPostgreSQLConfig(name, version, database, username, password string) DockerComposeConfig { 39 | services := make(map[string]Service) 40 | 41 | if database == "" { 42 | database = PostgreSQLDefaultDatabase 43 | } 44 | 45 | if username == "" { 46 | username = PostgreSQLDefaultUsername 47 | } 48 | 49 | if password == "" { 50 | password = PostgreSQLDefaultPassword 51 | } 52 | 53 | if version == "" { 54 | version = PostgreSQLDockerImageVersion 55 | } 56 | 57 | services[name] = Service{ 58 | Image: fmt.Sprintf("%s:%s", PostgreSQLDockerImage, version), 59 | Restart: PostgreSQLRestartPolicy, 60 | Ports: []string{PostgreSQLPort}, 61 | Environment: []string{ 62 | fmt.Sprintf("POSTGRES_DB=%s", database), 63 | fmt.Sprintf("POSTGRES_USER=%s", username), 64 | fmt.Sprintf("POSTGRES_PASSWORD=%s", password), 65 | }, 66 | } 67 | 68 | return DockerComposeConfig{ 69 | Version: "3", 70 | Services: services, 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /core/definition/postgresql_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitPostgreSQL test cases 16 | func TestUnitPostgreSQL(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestPostgreSQL", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | postgresql := GetPostgreSQLConfig("postgresql", "", "db", "username", "password") 22 | result, err := postgresql.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", PostgreSQLDockerImage, PostgreSQLDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, PostgreSQLPort))).Equal(true) 26 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", PostgreSQLRestartPolicy))).Equal(true) 27 | g.Assert(strings.Contains(result, "POSTGRES_DB=db")).Equal(true) 28 | g.Assert(strings.Contains(result, "POSTGRES_USER=username")).Equal(true) 29 | g.Assert(strings.Contains(result, "POSTGRES_PASSWORD=password")).Equal(true) 30 | g.Assert(err).Equal(nil) 31 | 32 | postgresql = GetPostgreSQLConfig("postgresql", "", "", "", "") 33 | result, err = postgresql.ToString() 34 | 35 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", PostgreSQLDockerImage, PostgreSQLDockerImageVersion)))).Equal(true) 36 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, PostgreSQLPort))).Equal(true) 37 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", PostgreSQLRestartPolicy))).Equal(true) 38 | g.Assert(strings.Contains(result, fmt.Sprintf("POSTGRES_DB=%s", PostgreSQLDefaultDatabase))).Equal(true) 39 | g.Assert(strings.Contains(result, fmt.Sprintf("POSTGRES_USER=%s", PostgreSQLDefaultUsername))).Equal(true) 40 | g.Assert(strings.Contains(result, fmt.Sprintf("POSTGRES_PASSWORD=%s", PostgreSQLDefaultPassword))).Equal(true) 41 | g.Assert(err).Equal(nil) 42 | }) 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /core/definition/prometheus.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // PrometheusService const 13 | PrometheusService = "prometheus" 14 | 15 | // PrometheusPort const 16 | PrometheusPort = "9090" 17 | 18 | // PrometheusDockerImage const 19 | PrometheusDockerImage = "prom/prometheus" 20 | 21 | // PrometheusDockerImageVersion const 22 | PrometheusDockerImageVersion = "v2.28.1" 23 | 24 | // PrometheusRestartPolicy const 25 | PrometheusRestartPolicy = "unless-stopped" 26 | 27 | // PrometheusDefaultConfig const 28 | PrometheusDefaultConfig = "Z2xvYmFsOgogIGV2YWx1YXRpb25faW50ZXJ2YWw6IDE1cwogIHNjcmFwZV9pbnRlcnZhbDogMTVzCnJ1bGVfZmlsZXM6IH4Kc2NyYXBlX2NvbmZpZ3M6CiAgLQogICAgam9iX25hbWU6IHByb21ldGhldXMKICAgIHNjcmFwZV9pbnRlcnZhbDogNXMKICAgIHN0YXRpY19jb25maWdzOgogICAgICAtCiAgICAgICAgdGFyZ2V0czoKICAgICAgICAgIC0gImxvY2FsaG9zdDo5MDkwIg==" 29 | ) 30 | 31 | // GetPrometheusConfig gets yaml definition object 32 | func GetPrometheusConfig(name, version, configPath string) DockerComposeConfig { 33 | services := make(map[string]Service) 34 | 35 | if version == "" { 36 | version = PrometheusDockerImageVersion 37 | } 38 | 39 | services[name] = Service{ 40 | Image: fmt.Sprintf("%s:%s", PrometheusDockerImage, version), 41 | Restart: PrometheusRestartPolicy, 42 | Ports: []string{PrometheusPort}, 43 | Volumes: []string{ 44 | fmt.Sprintf("%s:/etc/prometheus/prometheus.yml", configPath), 45 | }, 46 | } 47 | 48 | return DockerComposeConfig{ 49 | Version: "3", 50 | Services: services, 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /core/definition/prometheus_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitPrometheus test cases 16 | func TestUnitPrometheus(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestPrometheus", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | prometheus := GetPrometheusConfig("prometheus", "", "/etc/peanut/storage/da2ce8ac-d33f-4dd9-a345-d76f2a4336be.yml") 22 | result, err := prometheus.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", PrometheusDockerImage, PrometheusDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, PrometheusPort))).Equal(true) 26 | g.Assert(strings.Contains( 27 | result, 28 | "- /etc/peanut/storage/da2ce8ac-d33f-4dd9-a345-d76f2a4336be.yml:/etc/prometheus/prometheus.yml", 29 | )).Equal(true) 30 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", PrometheusRestartPolicy))).Equal(true) 31 | g.Assert(err).Equal(nil) 32 | }) 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /core/definition/rabbitmq.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // RabbitMQService const 13 | RabbitMQService = "rabbitmq" 14 | 15 | // RabbitMQAMQPPort const 16 | RabbitMQAMQPPort = "5672" 17 | 18 | // RabbitMQDashboardPort const 19 | RabbitMQDashboardPort = "15672" 20 | 21 | // RabbitMQDockerImage const 22 | RabbitMQDockerImage = "rabbitmq" 23 | 24 | // RabbitMQDockerImageVersion const 25 | RabbitMQDockerImageVersion = "3.8-management" 26 | 27 | // RabbitMQRestartPolicy const 28 | RabbitMQRestartPolicy = "unless-stopped" 29 | 30 | // RabbitMQDefaultUsername const 31 | RabbitMQDefaultUsername = "guest" 32 | 33 | // RabbitMQDefaultPassword const 34 | RabbitMQDefaultPassword = "guest" 35 | ) 36 | 37 | // GetRabbitMQConfig gets yaml definition object 38 | func GetRabbitMQConfig(name, version string) DockerComposeConfig { 39 | services := make(map[string]Service) 40 | 41 | if version == "" { 42 | version = RabbitMQDockerImageVersion 43 | } 44 | 45 | services[name] = Service{ 46 | Image: fmt.Sprintf("%s:%s", RabbitMQDockerImage, version), 47 | Restart: RabbitMQRestartPolicy, 48 | Ports: []string{ 49 | RabbitMQAMQPPort, 50 | RabbitMQDashboardPort, 51 | }, 52 | } 53 | 54 | return DockerComposeConfig{ 55 | Version: "3", 56 | Services: services, 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core/definition/rabbitmq_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitRabbitMQ test cases 16 | func TestUnitRabbitMQ(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestRabbitMQ", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | rabbitmq := GetRabbitMQConfig("rabbitmq", "") 22 | result, err := rabbitmq.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", RabbitMQDockerImage, RabbitMQDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, RabbitMQAMQPPort))).Equal(true) 26 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, RabbitMQDashboardPort))).Equal(true) 27 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", RabbitMQRestartPolicy))).Equal(true) 28 | g.Assert(err).Equal(nil) 29 | }) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /core/definition/redis.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // RedisService const 13 | RedisService = "redis" 14 | 15 | // RedisPort const 16 | RedisPort = "6379" 17 | 18 | // RedisDockerImage const 19 | RedisDockerImage = "bitnami/redis" 20 | 21 | // RedisDockerImageVersion const 22 | RedisDockerImageVersion = "6.2.4" 23 | 24 | // RedisRestartPolicy const 25 | RedisRestartPolicy = "unless-stopped" 26 | 27 | // RedisDefaultPassword const 28 | RedisDefaultPassword = "" 29 | ) 30 | 31 | // GetRedisConfig gets yaml definition object 32 | func GetRedisConfig(name, version, password string) DockerComposeConfig { 33 | services := make(map[string]Service) 34 | 35 | envVar1 := "ALLOW_EMPTY_PASSWORD=yes" 36 | 37 | if password != "" { 38 | envVar1 = fmt.Sprintf("REDIS_PASSWORD=%s", password) 39 | } 40 | 41 | if version == "" { 42 | version = RedisDockerImageVersion 43 | } 44 | 45 | services[name] = Service{ 46 | Image: fmt.Sprintf("%s:%s", RedisDockerImage, version), 47 | Restart: RedisRestartPolicy, 48 | Ports: []string{RedisPort}, 49 | Environment: []string{ 50 | envVar1, 51 | }, 52 | } 53 | 54 | return DockerComposeConfig{ 55 | Version: "3", 56 | Services: services, 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core/definition/redis_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitRedis test cases 16 | func TestUnitRedis(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestRedis", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | redis := GetRedisConfig("redis", "", "pass") 22 | result, err := redis.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", RedisDockerImage, RedisDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, RedisPort))).Equal(true) 26 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", RedisRestartPolicy))).Equal(true) 27 | g.Assert(strings.Contains(result, "REDIS_PASSWORD=pass")).Equal(true) 28 | g.Assert(err).Equal(nil) 29 | 30 | redis = GetRedisConfig("redis", "", "") 31 | result, err = redis.ToString() 32 | 33 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", RedisDockerImage, RedisDockerImageVersion)))).Equal(true) 34 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, RedisPort))).Equal(true) 35 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", RedisRestartPolicy))).Equal(true) 36 | g.Assert(strings.Contains(result, "ALLOW_EMPTY_PASSWORD=yes")).Equal(true) 37 | g.Assert(err).Equal(nil) 38 | }) 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /core/definition/registry.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // RegistryService const 13 | RegistryService = "registry" 14 | 15 | // RegistryPort const 16 | RegistryPort = "5000" 17 | 18 | // RegistryDockerImage const 19 | RegistryDockerImage = "registry" 20 | 21 | // RegistryDockerImageVersion const 22 | RegistryDockerImageVersion = "2" 23 | 24 | // RegistryRestartPolicy const 25 | RegistryRestartPolicy = "unless-stopped" 26 | ) 27 | 28 | // GetRegistryConfig gets yaml definition object 29 | func GetRegistryConfig(name, version string) DockerComposeConfig { 30 | services := make(map[string]Service) 31 | 32 | if version == "" { 33 | version = RegistryDockerImageVersion 34 | } 35 | 36 | services[name] = Service{ 37 | Image: fmt.Sprintf("%s:%s", RegistryDockerImage, version), 38 | Restart: RegistryRestartPolicy, 39 | Ports: []string{RegistryPort}, 40 | } 41 | 42 | return DockerComposeConfig{ 43 | Version: "3", 44 | Services: services, 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /core/definition/vault.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // VaultService const 13 | VaultService = "vault" 14 | 15 | // VaultHTTPPort const 16 | VaultHTTPPort = "8200" 17 | 18 | // VaultDockerImage const 19 | VaultDockerImage = "vault" 20 | 21 | // VaultDockerImageVersion const 22 | VaultDockerImageVersion = "1.7.3" 23 | 24 | // VaultRestartPolicy const 25 | VaultRestartPolicy = "unless-stopped" 26 | 27 | // VaultDefaultToken const 28 | VaultDefaultToken = "peanut" 29 | ) 30 | 31 | // GetVaultConfig gets yaml definition object 32 | func GetVaultConfig(name, version, token string) DockerComposeConfig { 33 | services := make(map[string]Service) 34 | 35 | if token == "" { 36 | token = VaultDefaultToken 37 | } 38 | 39 | if version == "" { 40 | version = VaultDockerImageVersion 41 | } 42 | 43 | services[name] = Service{ 44 | Image: fmt.Sprintf("%s:%s", VaultDockerImage, version), 45 | Restart: VaultRestartPolicy, 46 | Ports: []string{VaultHTTPPort}, 47 | Environment: []string{fmt.Sprintf("VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:%s", VaultHTTPPort)}, 48 | Command: fmt.Sprintf("vault server -dev -dev-root-token-id=%s", token), 49 | } 50 | 51 | return DockerComposeConfig{ 52 | Version: "3", 53 | Services: services, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /core/definition/vault_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitVault test cases 16 | func TestUnitVault(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestVault", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | vault := GetVaultConfig("vault", "", "token") 22 | result, err := vault.ToString() 23 | 24 | g.Assert(strings.Contains( 25 | result, 26 | fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", VaultDockerImage, VaultDockerImageVersion)), 27 | )).Equal(true) 28 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, VaultHTTPPort))).Equal(true) 29 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", VaultRestartPolicy))).Equal(true) 30 | g.Assert(strings.Contains(result, fmt.Sprintf("VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:%s", VaultHTTPPort))).Equal(true) 31 | g.Assert(strings.Contains(result, "vault server -dev -dev-root-token-id=token")).Equal(true) 32 | g.Assert(err).Equal(nil) 33 | }) 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /core/definition/zipkin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | const ( 12 | // ZipkinService const 13 | ZipkinService = "zipkin" 14 | 15 | // ZipkinPort const 16 | ZipkinPort = "9411" 17 | 18 | // ZipkinDockerImage const 19 | ZipkinDockerImage = "openzipkin/zipkin" 20 | 21 | // ZipkinDockerImageVersion const 22 | ZipkinDockerImageVersion = "2.23" 23 | 24 | // ZipkinRestartPolicy const 25 | ZipkinRestartPolicy = "unless-stopped" 26 | ) 27 | 28 | // GetZipkinConfig gets yaml definition object 29 | func GetZipkinConfig(name, version string) DockerComposeConfig { 30 | services := make(map[string]Service) 31 | 32 | if version == "" { 33 | version = ZipkinDockerImageVersion 34 | } 35 | 36 | services[name] = Service{ 37 | Image: fmt.Sprintf("%s:%s", ZipkinDockerImage, version), 38 | Restart: ZipkinRestartPolicy, 39 | Ports: []string{ZipkinPort}, 40 | } 41 | 42 | return DockerComposeConfig{ 43 | Version: "3", 44 | Services: services, 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /core/definition/zipkin_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package definition 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/franela/goblin" 13 | ) 14 | 15 | // TestUnitZipkin test cases 16 | func TestUnitZipkin(t *testing.T) { 17 | g := goblin.Goblin(t) 18 | 19 | g.Describe("#TestZipkin", func() { 20 | g.It("It should satisfy all provided test cases", func() { 21 | zipkin := GetZipkinConfig("zipkin", "") 22 | result, err := zipkin.ToString() 23 | 24 | g.Assert(strings.Contains(result, fmt.Sprintf("image: %s", fmt.Sprintf("%s:%s", ZipkinDockerImage, ZipkinDockerImageVersion)))).Equal(true) 25 | g.Assert(strings.Contains(result, fmt.Sprintf(`- "%s"`, ZipkinPort))).Equal(true) 26 | g.Assert(strings.Contains(result, fmt.Sprintf("restart: %s", ZipkinRestartPolicy))).Equal(true) 27 | g.Assert(err).Equal(nil) 28 | }) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /core/driver/db_interface.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package driver 6 | 7 | import ( 8 | "go.etcd.io/etcd/client/v3" 9 | ) 10 | 11 | // Database interface 12 | type Database interface { 13 | Connect() error 14 | IsConnected() bool 15 | Put(key, value string) error 16 | PutWithLease(key, value string, leaseID clientv3.LeaseID) error 17 | Get(key string) (map[string]string, error) 18 | Delete(key string) (int64, error) 19 | CreateLease(seconds int64) (clientv3.LeaseID, error) 20 | RenewLease(leaseID clientv3.LeaseID) error 21 | GetKeys(key string) ([]string, error) 22 | Exists(key string) (bool, error) 23 | Close() 24 | } 25 | -------------------------------------------------------------------------------- /core/driver/etcd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package driver 6 | 7 | import ( 8 | "context" 9 | "fmt" 10 | "strings" 11 | "time" 12 | 13 | "github.com/clivern/peanut/core/util" 14 | 15 | "github.com/spf13/viper" 16 | "go.etcd.io/etcd/client/v3" 17 | ) 18 | 19 | // Etcd driver 20 | type Etcd struct { 21 | client *clientv3.Client 22 | } 23 | 24 | // NewEtcdDriver create a new instance 25 | func NewEtcdDriver() Database { 26 | return new(Etcd) 27 | } 28 | 29 | // Connect connect to etcd server 30 | func (e *Etcd) Connect() error { 31 | var err error 32 | 33 | e.client, err = clientv3.New(clientv3.Config{ 34 | Endpoints: strings.Split(viper.GetString("app.database.etcd.endpoints"), ","), 35 | DialTimeout: time.Duration(viper.GetInt("app.database.etcd.timeout")) * time.Second, 36 | Username: viper.GetString("app.database.etcd.username"), 37 | Password: viper.GetString("app.database.etcd.password"), 38 | }) 39 | 40 | if err != nil { 41 | return err 42 | } 43 | 44 | return nil 45 | } 46 | 47 | // IsConnected checks if there is an etcd connection 48 | func (e *Etcd) IsConnected() bool { 49 | return e.client != nil 50 | } 51 | 52 | // Put sets a record 53 | func (e *Etcd) Put(key, value string) error { 54 | ctx, cancel := context.WithTimeout( 55 | context.Background(), 56 | time.Duration(viper.GetInt("app.database.etcd.timeout"))*time.Second, 57 | ) 58 | 59 | _, err := e.client.Put(ctx, key, value) 60 | 61 | defer cancel() 62 | 63 | if err != nil { 64 | return err 65 | } 66 | 67 | return nil 68 | } 69 | 70 | // PutWithLease sets a record 71 | func (e *Etcd) PutWithLease(key, value string, leaseID clientv3.LeaseID) error { 72 | ctx, cancel := context.WithTimeout( 73 | context.Background(), 74 | time.Duration(viper.GetInt("app.database.etcd.timeout"))*time.Second, 75 | ) 76 | 77 | _, err := e.client.Put(ctx, key, value, clientv3.WithLease(leaseID)) 78 | 79 | defer cancel() 80 | 81 | if err != nil { 82 | return err 83 | } 84 | 85 | return nil 86 | } 87 | 88 | // Get gets a record value 89 | func (e *Etcd) Get(key string) (map[string]string, error) { 90 | result := make(map[string]string, 0) 91 | 92 | ctx, cancel := context.WithTimeout( 93 | context.Background(), 94 | time.Duration(viper.GetInt("app.database.etcd.timeout"))*time.Second, 95 | ) 96 | 97 | resp, err := e.client.Get(ctx, key, clientv3.WithPrefix()) 98 | 99 | defer cancel() 100 | 101 | if err != nil { 102 | return result, err 103 | } 104 | 105 | for _, ev := range resp.Kvs { 106 | result[string(ev.Key)] = string(ev.Value) 107 | } 108 | 109 | return result, nil 110 | } 111 | 112 | // Delete deletes a record 113 | func (e *Etcd) Delete(key string) (int64, error) { 114 | ctx, cancel := context.WithTimeout( 115 | context.Background(), 116 | time.Duration(viper.GetInt("app.database.etcd.timeout"))*time.Second, 117 | ) 118 | 119 | dresp, err := e.client.Delete(ctx, key, clientv3.WithPrefix()) 120 | 121 | defer cancel() 122 | 123 | if err != nil { 124 | return 0, err 125 | } 126 | 127 | return dresp.Deleted, nil 128 | } 129 | 130 | // CreateLease creates a lease 131 | func (e *Etcd) CreateLease(seconds int64) (clientv3.LeaseID, error) { 132 | var result clientv3.LeaseID 133 | 134 | ctx, cancel := context.WithTimeout( 135 | context.Background(), 136 | time.Duration(viper.GetInt("app.database.etcd.timeout"))*time.Second, 137 | ) 138 | 139 | resp, err := e.client.Grant(ctx, seconds) 140 | 141 | defer cancel() 142 | 143 | if err != nil { 144 | return result, err 145 | } 146 | 147 | return resp.ID, nil 148 | } 149 | 150 | // RenewLease renews a lease 151 | func (e *Etcd) RenewLease(leaseID clientv3.LeaseID) error { 152 | ctx, cancel := context.WithTimeout( 153 | context.Background(), 154 | time.Duration(viper.GetInt("app.database.etcd.timeout"))*time.Second, 155 | ) 156 | 157 | _, err := e.client.KeepAliveOnce(ctx, leaseID) 158 | 159 | defer cancel() 160 | 161 | if err != nil { 162 | return err 163 | } 164 | 165 | return nil 166 | } 167 | 168 | // GetKeys gets a record sub keys 169 | // This method will return only the keys under one key 170 | func (e *Etcd) GetKeys(key string) ([]string, error) { 171 | result := []string{} 172 | 173 | ctx, cancel := context.WithTimeout( 174 | context.Background(), 175 | time.Duration(viper.GetInt("app.database.etcd.timeout"))*time.Second, 176 | ) 177 | 178 | resp, err := e.client.Get(ctx, key, clientv3.WithPrefix()) 179 | 180 | defer cancel() 181 | 182 | if err != nil { 183 | return result, err 184 | } 185 | 186 | for _, ev := range resp.Kvs { 187 | sub := strings.Replace(string(ev.Key), util.EnsureTrailingSlash(key), "", -1) 188 | subKeys := strings.Split(sub, "/") 189 | newKey := fmt.Sprintf("%s%s", util.EnsureTrailingSlash(key), subKeys[0]) 190 | 191 | if !util.InArray(newKey, result) { 192 | result = append(result, newKey) 193 | } 194 | } 195 | 196 | return result, nil 197 | } 198 | 199 | // Exists checks if a record exists 200 | func (e *Etcd) Exists(key string) (bool, error) { 201 | result, err := e.Get(key) 202 | 203 | return len(result) > 0, err 204 | } 205 | 206 | // Close closes the etcd connection 207 | func (e *Etcd) Close() { 208 | e.client.Close() 209 | } 210 | -------------------------------------------------------------------------------- /core/driver/etcd_mock.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package driver 6 | 7 | import ( 8 | "github.com/stretchr/testify/mock" 9 | "go.etcd.io/etcd/client/v3" 10 | ) 11 | 12 | // EtcdMock type 13 | type EtcdMock struct { 14 | mock.Mock 15 | } 16 | 17 | // Connect mock 18 | func (e *EtcdMock) Connect() error { 19 | args := e.Called() 20 | return args.Error(0) 21 | } 22 | 23 | // IsConnected mock 24 | func (e *EtcdMock) IsConnected() bool { 25 | args := e.Called() 26 | return args.Bool(0) 27 | } 28 | 29 | // Put mock 30 | func (e *EtcdMock) Put(key, value string) error { 31 | args := e.Called(key, value) 32 | return args.Error(0) 33 | } 34 | 35 | // PutWithLease mock 36 | func (e *EtcdMock) PutWithLease(key, value string, leaseID clientv3.LeaseID) error { 37 | args := e.Called(key, value, leaseID) 38 | return args.Error(0) 39 | } 40 | 41 | // Get mock 42 | func (e *EtcdMock) Get(key string) (map[string]string, error) { 43 | args := e.Called(key) 44 | return args.Get(0).(map[string]string), args.Error(1) 45 | } 46 | 47 | // Delete mock 48 | func (e *EtcdMock) Delete(key string) (int64, error) { 49 | args := e.Called(key) 50 | return args.Get(0).(int64), args.Error(1) 51 | } 52 | 53 | // CreateLease mock 54 | func (e *EtcdMock) CreateLease(seconds int64) (clientv3.LeaseID, error) { 55 | args := e.Called(seconds) 56 | return args.Get(0).(clientv3.LeaseID), args.Error(1) 57 | } 58 | 59 | // RenewLease mock 60 | func (e *EtcdMock) RenewLease(leaseID clientv3.LeaseID) error { 61 | args := e.Called(leaseID) 62 | return args.Error(0) 63 | } 64 | 65 | // GetKeys mock 66 | func (e *EtcdMock) GetKeys(key string) ([]string, error) { 67 | args := e.Called(key) 68 | return args.Get(0).([]string), args.Error(1) 69 | } 70 | 71 | // Exists mock 72 | func (e *EtcdMock) Exists(key string) (bool, error) { 73 | args := e.Called(key) 74 | return args.Bool(0), args.Error(1) 75 | } 76 | 77 | // Close mock 78 | func (e *EtcdMock) Close() { 79 | // 80 | } 81 | -------------------------------------------------------------------------------- /core/middleware/auth.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package middleware 6 | 7 | import ( 8 | "net/http" 9 | "strings" 10 | 11 | "github.com/gin-gonic/gin" 12 | "github.com/spf13/viper" 13 | ) 14 | 15 | // Auth middleware 16 | func Auth() gin.HandlerFunc { 17 | return func(c *gin.Context) { 18 | path := c.Request.URL.Path 19 | 20 | if strings.Contains(path, "/api/") { 21 | apiKey := c.GetHeader("x-api-key") 22 | if apiKey != viper.GetString("app.api.key") && viper.GetString("app.api.key") != "" { 23 | c.AbortWithStatus(http.StatusUnauthorized) 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /core/middleware/correlation.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package middleware 6 | 7 | import ( 8 | "strings" 9 | 10 | "github.com/clivern/peanut/core/util" 11 | 12 | "github.com/gin-gonic/gin" 13 | ) 14 | 15 | // Correlation middleware 16 | func Correlation() gin.HandlerFunc { 17 | return func(c *gin.Context) { 18 | corralationID := c.GetHeader("x-correlation-id") 19 | 20 | if strings.TrimSpace(corralationID) == "" { 21 | c.Request.Header.Add("X-Correlation-ID", util.GenerateUUID4()) 22 | } 23 | c.Next() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/middleware/cors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package middleware 6 | 7 | import ( 8 | "net/http" 9 | 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | // Cors middleware 14 | func Cors() gin.HandlerFunc { 15 | return func(c *gin.Context) { 16 | c.Writer.Header().Set("Access-Control-Allow-Origin", "*") 17 | c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") 18 | c.Writer.Header().Set("Access-Control-Allow-Headers", "*") 19 | c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE") 20 | 21 | if c.Request.Method == "OPTIONS" { 22 | c.AbortWithStatus(http.StatusOK) 23 | return 24 | } 25 | 26 | c.Next() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/middleware/log.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package middleware 6 | 7 | import ( 8 | "bytes" 9 | "io/ioutil" 10 | 11 | "github.com/gin-gonic/gin" 12 | log "github.com/sirupsen/logrus" 13 | ) 14 | 15 | // Logger middleware 16 | // TODO Hide Secure Data from Logs 17 | func Logger() gin.HandlerFunc { 18 | return func(c *gin.Context) { 19 | // before request 20 | var bodyBytes []byte 21 | 22 | // Workaround for issue https://github.com/gin-gonic/gin/issues/1651 23 | if c.Request.Body != nil { 24 | bodyBytes, _ = ioutil.ReadAll(c.Request.Body) 25 | } 26 | 27 | c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) 28 | 29 | log.WithFields(log.Fields{ 30 | "correlation_id": c.GetHeader("x-correlation-id"), 31 | "http_method": c.Request.Method, 32 | "http_path": c.Request.URL.Path, 33 | "request_body": string(bodyBytes), 34 | }).Info("Request started") 35 | 36 | c.Next() 37 | 38 | // after request 39 | status := c.Writer.Status() 40 | size := c.Writer.Size() 41 | 42 | log.WithFields(log.Fields{ 43 | "correlation_id": c.GetHeader("x-correlation-id"), 44 | "http_status": status, 45 | "response_size": size, 46 | }).Info(`Request finished`) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/middleware/metric.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package middleware 6 | 7 | import ( 8 | "strconv" 9 | "time" 10 | 11 | "github.com/gin-gonic/gin" 12 | "github.com/prometheus/client_golang/prometheus" 13 | log "github.com/sirupsen/logrus" 14 | ) 15 | 16 | var ( 17 | httpRequests = prometheus.NewCounterVec( 18 | prometheus.CounterOpts{ 19 | Namespace: "peanut", 20 | Name: "total_http_requests", 21 | Help: "How many HTTP requests processed, partitioned by status code and HTTP method.", 22 | }, []string{"code", "method", "handler", "host", "url"}) 23 | 24 | requestDuration = prometheus.NewHistogramVec( 25 | prometheus.HistogramOpts{ 26 | Subsystem: "peanut", 27 | Name: "request_duration_seconds", 28 | Help: "The HTTP request latencies in seconds.", 29 | }, 30 | []string{"code", "method", "url"}, 31 | ) 32 | 33 | responseSize = prometheus.NewSummary( 34 | prometheus.SummaryOpts{ 35 | Namespace: "peanut", 36 | Name: "response_size_bytes", 37 | Help: "The HTTP response sizes in bytes.", 38 | }, 39 | ) 40 | ) 41 | 42 | func init() { 43 | prometheus.MustRegister(httpRequests) 44 | prometheus.MustRegister(requestDuration) 45 | prometheus.MustRegister(responseSize) 46 | } 47 | 48 | // Metric middleware 49 | func Metric() gin.HandlerFunc { 50 | return func(c *gin.Context) { 51 | // before request 52 | start := time.Now() 53 | 54 | c.Next() 55 | 56 | // after request 57 | elapsed := float64(time.Since(start)) / float64(time.Second) 58 | 59 | log.WithFields(log.Fields{ 60 | "correlation_id": c.GetHeader("x-correlation-id"), 61 | }).Info(`Collecting metrics`) 62 | 63 | // Collect Metrics 64 | httpRequests.WithLabelValues( 65 | strconv.Itoa(c.Writer.Status()), 66 | c.Request.Method, 67 | c.HandlerName(), 68 | c.Request.Host, 69 | c.Request.URL.Path, 70 | ).Inc() 71 | 72 | requestDuration.WithLabelValues( 73 | strconv.Itoa(c.Writer.Status()), 74 | c.Request.Method, 75 | c.Request.URL.Path, 76 | ).Observe(elapsed) 77 | 78 | responseSize.Observe(float64(c.Writer.Size())) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /core/model/README.md: -------------------------------------------------------------------------------- 1 | ### Data Model 2 | 3 | -------------------------------------------------------------------------------- /core/model/job.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package model 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "time" 11 | 12 | "github.com/clivern/peanut/core/driver" 13 | "github.com/clivern/peanut/core/util" 14 | 15 | log "github.com/sirupsen/logrus" 16 | "github.com/spf13/viper" 17 | ) 18 | 19 | const ( 20 | // PendingStatus constant 21 | PendingStatus = "PENDING" 22 | 23 | // FailedStatus constant 24 | FailedStatus = "FAILED" 25 | 26 | // SuccessStatus constant 27 | SuccessStatus = "SUCCESS" 28 | 29 | // DeployJob constant 30 | DeployJob = "@deployService" 31 | 32 | // DestroyJob constant 33 | DestroyJob = "@destroyService" 34 | ) 35 | 36 | // JobRecord type 37 | type JobRecord struct { 38 | ID string `json:"id"` 39 | Action string `json:"action"` 40 | Service ServiceRecord `json:"service"` 41 | Status string `json:"status"` 42 | CreatedAt int64 `json:"createdAt"` 43 | UpdatedAt int64 `json:"updatedAt"` 44 | } 45 | 46 | // Job type 47 | type Job struct { 48 | db driver.Database 49 | } 50 | 51 | // NewJobStore creates a new instance 52 | func NewJobStore(db driver.Database) *Job { 53 | result := new(Job) 54 | result.db = db 55 | 56 | return result 57 | } 58 | 59 | // CreateRecord stores a job record 60 | func (j *Job) CreateRecord(record JobRecord) error { 61 | record.CreatedAt = time.Now().Unix() 62 | record.UpdatedAt = time.Now().Unix() 63 | record.Service.CreatedAt = time.Now().Unix() 64 | record.Service.UpdatedAt = time.Now().Unix() 65 | 66 | result, err := util.ConvertToJSON(record) 67 | 68 | if err != nil { 69 | return err 70 | } 71 | 72 | log.WithFields(log.Fields{ 73 | "job_id": record.ID, 74 | "service_id": record.Service.ID, 75 | }).Debug("Create a job record") 76 | 77 | // store job record data 78 | err = j.db.Put(fmt.Sprintf( 79 | "%s/service/%s/job/%s/j-data", 80 | viper.GetString("app.database.etcd.databaseName"), 81 | record.Service.ID, 82 | record.ID, 83 | ), result) 84 | 85 | if err != nil { 86 | return err 87 | } 88 | 89 | return nil 90 | } 91 | 92 | // UpdateRecord updates a job record 93 | func (j *Job) UpdateRecord(record JobRecord) error { 94 | record.UpdatedAt = time.Now().Unix() 95 | 96 | result, err := util.ConvertToJSON(record) 97 | 98 | if err != nil { 99 | return err 100 | } 101 | 102 | log.WithFields(log.Fields{ 103 | "job_id": record.ID, 104 | "service_id": record.Service.ID, 105 | }).Debug("Update a job record") 106 | 107 | // store job record data 108 | err = j.db.Put(fmt.Sprintf( 109 | "%s/service/%s/job/%s/j-data", 110 | viper.GetString("app.database.etcd.databaseName"), 111 | record.Service.ID, 112 | record.ID, 113 | ), result) 114 | 115 | if err != nil { 116 | return err 117 | } 118 | 119 | return nil 120 | } 121 | 122 | // GetRecord gets job record data 123 | func (j *Job) GetRecord(serviceID, jobID string) (JobRecord, error) { 124 | recordData := &JobRecord{} 125 | 126 | log.WithFields(log.Fields{ 127 | "job_id": jobID, 128 | "service_id": serviceID, 129 | }).Debug("Get a job record data") 130 | 131 | data, err := j.db.Get(fmt.Sprintf( 132 | "%s/service/%s/job/%s/j-data", 133 | viper.GetString("app.database.etcd.databaseName"), 134 | serviceID, 135 | jobID, 136 | )) 137 | 138 | if err != nil { 139 | return *recordData, err 140 | } 141 | 142 | for k, v := range data { 143 | // Check if it is the data key 144 | if strings.Contains(k, "/j-data") { 145 | err = util.LoadFromJSON(recordData, []byte(v)) 146 | 147 | if err != nil { 148 | return *recordData, err 149 | } 150 | 151 | return *recordData, nil 152 | } 153 | } 154 | 155 | return *recordData, fmt.Errorf( 156 | "Unable to find job record with id: %s and service id: %s", 157 | jobID, 158 | serviceID, 159 | ) 160 | } 161 | 162 | // DeleteRecord deletes a job record 163 | func (j *Job) DeleteRecord(serviceID, jobID string) (bool, error) { 164 | 165 | log.WithFields(log.Fields{ 166 | "job_id": jobID, 167 | "service_id": serviceID, 168 | }).Debug("Delete a job record") 169 | 170 | count, err := j.db.Delete(fmt.Sprintf( 171 | "%s/service/%s/job/%s", 172 | viper.GetString("app.database.etcd.databaseName"), 173 | serviceID, 174 | jobID, 175 | )) 176 | 177 | if err != nil { 178 | return false, err 179 | } 180 | 181 | return count > 0, nil 182 | } 183 | -------------------------------------------------------------------------------- /core/model/job_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package model 6 | -------------------------------------------------------------------------------- /core/model/metric.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package model 6 | 7 | import ( 8 | "encoding/json" 9 | "strconv" 10 | 11 | "github.com/prometheus/client_golang/prometheus" 12 | ) 13 | 14 | const ( 15 | // COUNTER is a Prometheus COUNTER metric 16 | COUNTER string = "counter" 17 | // GAUGE is a Prometheus GAUGE metric 18 | GAUGE string = "gauge" 19 | // HISTOGRAM is a Prometheus HISTOGRAM metric 20 | HISTOGRAM string = "histogram" 21 | // SUMMARY is a Prometheus SUMMARY metric 22 | SUMMARY string = "summary" 23 | ) 24 | 25 | // Metric struct 26 | type Metric struct { 27 | Type string `json:"type"` 28 | Name string `json:"name"` 29 | Help string `json:"help"` 30 | Method string `json:"method"` 31 | Value string `json:"value"` 32 | Labels prometheus.Labels `json:"labels"` 33 | Buckets []float64 `json:"buckets"` 34 | } 35 | 36 | // LoadFromJSON update object from json 37 | func (m *Metric) LoadFromJSON(data []byte) (bool, error) { 38 | err := json.Unmarshal(data, &m) 39 | if err != nil { 40 | return false, err 41 | } 42 | return true, nil 43 | } 44 | 45 | // ConvertToJSON convert object to json 46 | func (m *Metric) ConvertToJSON() (string, error) { 47 | data, err := json.Marshal(&m) 48 | if err != nil { 49 | return "", err 50 | } 51 | return string(data), nil 52 | } 53 | 54 | // LabelKeys gets a list of label keys 55 | func (m *Metric) LabelKeys() []string { 56 | keys := []string{} 57 | 58 | for k := range m.Labels { 59 | keys = append(keys, k) 60 | } 61 | 62 | return keys 63 | } 64 | 65 | // LabelValues gets a list of label values 66 | func (m *Metric) LabelValues() []string { 67 | values := []string{} 68 | 69 | for _, v := range m.Labels { 70 | values = append(values, v) 71 | } 72 | 73 | return values 74 | } 75 | 76 | // GetValueAsFloat gets a list of label values 77 | func (m *Metric) GetValueAsFloat() (float64, error) { 78 | value, err := strconv.ParseFloat(m.Value, 64) 79 | 80 | if err != nil { 81 | return 0, nil 82 | } 83 | 84 | return value, nil 85 | } 86 | -------------------------------------------------------------------------------- /core/model/option.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package model 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "time" 11 | 12 | "github.com/clivern/peanut/core/driver" 13 | "github.com/clivern/peanut/core/util" 14 | 15 | log "github.com/sirupsen/logrus" 16 | "github.com/spf13/viper" 17 | ) 18 | 19 | // Option type 20 | type Option struct { 21 | db driver.Database 22 | } 23 | 24 | // OptionData struct 25 | type OptionData struct { 26 | Key string `json:"key"` 27 | Value string `json:"value"` 28 | CreatedAt int64 `json:"createdAt"` 29 | UpdatedAt int64 `json:"updatedAt"` 30 | } 31 | 32 | // NewOptionStore creates a new instance 33 | func NewOptionStore(db driver.Database) *Option { 34 | result := new(Option) 35 | result.db = db 36 | 37 | return result 38 | } 39 | 40 | // CreateOption stores an option 41 | func (o *Option) CreateOption(option OptionData) error { 42 | 43 | option.CreatedAt = time.Now().Unix() 44 | option.UpdatedAt = time.Now().Unix() 45 | 46 | result, err := util.ConvertToJSON(option) 47 | 48 | if err != nil { 49 | return err 50 | } 51 | 52 | log.WithFields(log.Fields{ 53 | "option_key": option.Key, 54 | }).Debug("Create an option") 55 | 56 | // store option data 57 | err = o.db.Put(fmt.Sprintf( 58 | "%s/option/%s/o-data", 59 | viper.GetString("app.database.etcd.databaseName"), 60 | option.Key, 61 | ), result) 62 | 63 | if err != nil { 64 | return err 65 | } 66 | 67 | return nil 68 | } 69 | 70 | // UpdateOptionByKey updates an option by key 71 | func (o *Option) UpdateOptionByKey(option OptionData) error { 72 | option.UpdatedAt = time.Now().Unix() 73 | 74 | result, err := util.ConvertToJSON(option) 75 | 76 | if err != nil { 77 | return err 78 | } 79 | 80 | log.WithFields(log.Fields{ 81 | "option_key": option.Key, 82 | }).Debug("Update an option") 83 | 84 | // store option data 85 | err = o.db.Put(fmt.Sprintf( 86 | "%s/option/%s/o-data", 87 | viper.GetString("app.database.etcd.databaseName"), 88 | option.Key, 89 | ), result) 90 | 91 | if err != nil { 92 | return err 93 | } 94 | 95 | return nil 96 | } 97 | 98 | // UpdateOptions update options 99 | func (o *Option) UpdateOptions(options []OptionData) error { 100 | log.Debug("Update options") 101 | 102 | for _, option := range options { 103 | err := o.UpdateOptionByKey(option) 104 | 105 | if err != nil { 106 | return err 107 | } 108 | } 109 | 110 | return nil 111 | } 112 | 113 | // GetOptionByKey gets an option by a key 114 | func (o *Option) GetOptionByKey(key string) (OptionData, error) { 115 | optionResult := &OptionData{} 116 | 117 | log.WithFields(log.Fields{ 118 | "option_key": key, 119 | }).Debug("Get an option") 120 | 121 | data, err := o.db.Get(fmt.Sprintf( 122 | "%s/option/%s/o-data", 123 | viper.GetString("app.database.etcd.databaseName"), 124 | key, 125 | )) 126 | 127 | if err != nil { 128 | return *optionResult, err 129 | } 130 | 131 | for k, v := range data { 132 | // Check if it is the data key 133 | if strings.Contains(k, "/o-data") { 134 | err = util.LoadFromJSON(optionResult, []byte(v)) 135 | 136 | if err != nil { 137 | return *optionResult, err 138 | } 139 | 140 | return *optionResult, nil 141 | } 142 | } 143 | 144 | return *optionResult, fmt.Errorf( 145 | "Unable to find an option with a key: %s", 146 | key, 147 | ) 148 | } 149 | 150 | // DeleteOptionByKey deletes an option by a key 151 | func (o *Option) DeleteOptionByKey(key string) (bool, error) { 152 | 153 | log.WithFields(log.Fields{ 154 | "option_key": key, 155 | }).Debug("Delete an option") 156 | 157 | count, err := o.db.Delete(fmt.Sprintf( 158 | "%s/option/%s", 159 | viper.GetString("app.database.etcd.databaseName"), 160 | key, 161 | )) 162 | 163 | if err != nil { 164 | return false, err 165 | } 166 | 167 | return count > 0, nil 168 | } 169 | -------------------------------------------------------------------------------- /core/model/option_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package model 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | "time" 11 | 12 | "github.com/clivern/peanut/core/driver" 13 | "github.com/clivern/peanut/pkg" 14 | 15 | "github.com/franela/goblin" 16 | "github.com/spf13/viper" 17 | ) 18 | 19 | // TestIntegrationOptionMethods test cases 20 | func TestIntegrationOptionMethods(t *testing.T) { 21 | // Skip if -short flag exist 22 | if testing.Short() { 23 | t.Skip("skipping test in short mode.") 24 | } 25 | 26 | baseDir := pkg.GetBaseDir("cache") 27 | pkg.LoadConfigs(fmt.Sprintf("%s/config.dist.yml", baseDir)) 28 | 29 | g := goblin.Goblin(t) 30 | 31 | db := driver.NewEtcdDriver() 32 | db.Connect() 33 | 34 | defer db.Close() 35 | 36 | option := NewOptionStore(db) 37 | 38 | // Cleanup 39 | db.Delete(viper.GetString("app.database.etcd.databaseName")) 40 | 41 | time.Sleep(3 * time.Second) 42 | 43 | g.Describe("#CreateOption", func() { 44 | g.It("It should satisfy test cases", func() { 45 | err := option.CreateOption(OptionData{ 46 | Key: "key_01", 47 | Value: "value_01", 48 | }) 49 | 50 | g.Assert(err).Equal(nil) 51 | 52 | err = option.CreateOption(OptionData{ 53 | Key: "key_02", 54 | Value: "value_02", 55 | }) 56 | 57 | g.Assert(err).Equal(nil) 58 | }) 59 | }) 60 | 61 | g.Describe("#UpdateOptionByKey", func() { 62 | g.It("It should satisfy test cases", func() { 63 | err := option.UpdateOptionByKey(OptionData{ 64 | Key: "key_02", 65 | Value: "new_value_02", 66 | CreatedAt: time.Now().Unix(), 67 | UpdatedAt: time.Now().Unix(), 68 | }) 69 | 70 | g.Assert(err).Equal(nil) 71 | 72 | err = option.UpdateOptionByKey(OptionData{ 73 | Key: "", 74 | Value: "value_03", 75 | CreatedAt: time.Now().Unix(), 76 | UpdatedAt: time.Now().Unix(), 77 | }) 78 | 79 | g.Assert(err).Equal(nil) 80 | }) 81 | }) 82 | 83 | g.Describe("#UpdateOptions", func() { 84 | g.It("It should satisfy test cases", func() { 85 | err := option.UpdateOptions([]OptionData{ 86 | OptionData{ 87 | Key: "key_03", 88 | Value: "new_value_03", 89 | CreatedAt: time.Now().Unix(), 90 | UpdatedAt: time.Now().Unix(), 91 | }, 92 | OptionData{ 93 | Key: "key_04", 94 | Value: "value_04", 95 | CreatedAt: time.Now().Unix(), 96 | UpdatedAt: time.Now().Unix(), 97 | }, 98 | }) 99 | 100 | g.Assert(err).Equal(nil) 101 | }) 102 | }) 103 | 104 | g.Describe("#GetOptionByKey", func() { 105 | g.It("It should satisfy test cases", func() { 106 | value, err := option.GetOptionByKey("key_03") 107 | 108 | g.Assert(value.Value).Equal("new_value_03") 109 | g.Assert(err).Equal(nil) 110 | }) 111 | }) 112 | 113 | g.Describe("#DeleteOptionByKey", func() { 114 | g.It("It should satisfy test cases", func() { 115 | ok, err := option.DeleteOptionByKey("key_04") 116 | 117 | g.Assert(ok).Equal(true) 118 | g.Assert(err).Equal(nil) 119 | }) 120 | }) 121 | } 122 | -------------------------------------------------------------------------------- /core/model/service.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package model 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "time" 11 | 12 | "github.com/clivern/peanut/core/driver" 13 | "github.com/clivern/peanut/core/util" 14 | 15 | log "github.com/sirupsen/logrus" 16 | "github.com/spf13/viper" 17 | ) 18 | 19 | // ServiceRecord type 20 | type ServiceRecord struct { 21 | ID string `json:"id"` 22 | Service string `json:"service"` 23 | Version string `json:"version"` 24 | Configs map[string]string `json:"configs"` 25 | DeleteAfter string `json:"deleteAfter"` 26 | CreatedAt int64 `json:"createdAt"` 27 | UpdatedAt int64 `json:"updatedAt"` 28 | } 29 | 30 | // Service type 31 | type Service struct { 32 | db driver.Database 33 | } 34 | 35 | // NewServiceStore creates a new instance 36 | func NewServiceStore(db driver.Database) *Service { 37 | result := new(Service) 38 | result.db = db 39 | 40 | return result 41 | } 42 | 43 | // CreateRecord stores a service record 44 | func (s *Service) CreateRecord(record ServiceRecord) error { 45 | record.CreatedAt = time.Now().Unix() 46 | record.UpdatedAt = time.Now().Unix() 47 | 48 | result, err := util.ConvertToJSON(record) 49 | 50 | if err != nil { 51 | return err 52 | } 53 | 54 | log.WithFields(log.Fields{ 55 | "service_id": record.ID, 56 | "service": record.Service, 57 | }).Debug("Create a service record") 58 | 59 | // store service record data 60 | err = s.db.Put(fmt.Sprintf( 61 | "%s/service/%s/s-data", 62 | viper.GetString("app.database.etcd.databaseName"), 63 | record.ID, 64 | ), result) 65 | 66 | if err != nil { 67 | return err 68 | } 69 | 70 | return nil 71 | } 72 | 73 | // UpdateRecord updates a service record 74 | func (s *Service) UpdateRecord(record ServiceRecord) error { 75 | record.UpdatedAt = time.Now().Unix() 76 | 77 | result, err := util.ConvertToJSON(record) 78 | 79 | if err != nil { 80 | return err 81 | } 82 | 83 | log.WithFields(log.Fields{ 84 | "service_id": record.ID, 85 | "service": record.Service, 86 | }).Debug("Update a service record") 87 | 88 | // store service record data 89 | err = s.db.Put(fmt.Sprintf( 90 | "%s/service/%s/s-data", 91 | viper.GetString("app.database.etcd.databaseName"), 92 | record.ID, 93 | ), result) 94 | 95 | if err != nil { 96 | return err 97 | } 98 | 99 | return nil 100 | } 101 | 102 | // GetRecord gets service record data 103 | func (s *Service) GetRecord(serviceID string) (ServiceRecord, error) { 104 | recordData := &ServiceRecord{} 105 | 106 | log.WithFields(log.Fields{ 107 | "service_id": serviceID, 108 | }).Debug("Get a service record data") 109 | 110 | data, err := s.db.Get(fmt.Sprintf( 111 | "%s/service/%s/s-data", 112 | viper.GetString("app.database.etcd.databaseName"), 113 | serviceID, 114 | )) 115 | 116 | if err != nil { 117 | return *recordData, err 118 | } 119 | 120 | for k, v := range data { 121 | // Check if it is the data key 122 | if strings.Contains(k, "/s-data") { 123 | err = util.LoadFromJSON(recordData, []byte(v)) 124 | 125 | if err != nil { 126 | return *recordData, err 127 | } 128 | 129 | return *recordData, nil 130 | } 131 | } 132 | 133 | return *recordData, fmt.Errorf( 134 | "Unable to find service record with id: %s", 135 | serviceID, 136 | ) 137 | } 138 | 139 | // GetRecords get services 140 | func (s *Service) GetRecords() ([]ServiceRecord, error) { 141 | records := make([]ServiceRecord, 0) 142 | 143 | log.Debug("Get services") 144 | 145 | data, err := s.db.Get(fmt.Sprintf( 146 | "%s/service", 147 | viper.GetString("app.database.etcd.databaseName"), 148 | )) 149 | 150 | if err != nil { 151 | return records, err 152 | } 153 | 154 | for k, v := range data { 155 | if strings.Contains(k, "/s-data") { 156 | recordData := &ServiceRecord{} 157 | 158 | err = util.LoadFromJSON(recordData, []byte(v)) 159 | 160 | if err != nil { 161 | return records, err 162 | } 163 | 164 | records = append(records, *recordData) 165 | } 166 | } 167 | 168 | return records, nil 169 | } 170 | 171 | // DeleteRecord deletes a service record 172 | func (s *Service) DeleteRecord(serviceID string) (bool, error) { 173 | 174 | log.WithFields(log.Fields{ 175 | "service_id": serviceID, 176 | }).Debug("Delete a service record") 177 | 178 | count, err := s.db.Delete(fmt.Sprintf( 179 | "%s/service/%s", 180 | viper.GetString("app.database.etcd.databaseName"), 181 | serviceID, 182 | )) 183 | 184 | if err != nil { 185 | return false, err 186 | } 187 | 188 | return count > 0, nil 189 | } 190 | -------------------------------------------------------------------------------- /core/model/service_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package model 6 | -------------------------------------------------------------------------------- /core/runtime/base.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package runtime 6 | 7 | // Containerization interface 8 | type Containerization interface { 9 | Deploy(serviceID, service, version string, configs map[string]string) (map[string]string, error) 10 | Destroy(serviceID, service, version string, configs map[string]string) error 11 | } 12 | -------------------------------------------------------------------------------- /core/runtime/docker_compose_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package runtime 6 | -------------------------------------------------------------------------------- /core/service/docker_hub.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package service 6 | 7 | import ( 8 | "context" 9 | "encoding/json" 10 | "fmt" 11 | ) 12 | 13 | // DockerHubAPI const 14 | const DockerHubAPI = "https://registry.hub.docker.com/v2/" 15 | 16 | // DockerHub struct 17 | type DockerHub struct { 18 | httpClient *HTTPClient 19 | } 20 | 21 | // TagsResponse type 22 | type TagsResponse struct { 23 | Results []struct { 24 | ID int `json:"id"` 25 | Name string `json:"name"` 26 | } `json:"results"` 27 | } 28 | 29 | // LoadFromJSON update object from json 30 | func (t *TagsResponse) LoadFromJSON(data []byte) error { 31 | err := json.Unmarshal(data, &t) 32 | 33 | if err != nil { 34 | return err 35 | } 36 | 37 | return nil 38 | } 39 | 40 | // ConvertToJSON convert object to json 41 | func (t *TagsResponse) ConvertToJSON() (string, error) { 42 | data, err := json.Marshal(&t) 43 | if err != nil { 44 | return "", err 45 | } 46 | return string(data), nil 47 | } 48 | 49 | // NewDockerHub creates an instance of http client 50 | func NewDockerHub(httpClient *HTTPClient) *DockerHub { 51 | return &DockerHub{ 52 | httpClient: httpClient, 53 | } 54 | } 55 | 56 | // GetTags get image tags 57 | func (d *DockerHub) GetTags(ctx context.Context, org, image string) ([]string, error) { 58 | var tags []string 59 | 60 | tagsResponse := &TagsResponse{} 61 | 62 | response, err := d.httpClient.Get( 63 | ctx, 64 | fmt.Sprintf("%s/repositories/%s/%s/tags/?page_size=1000", DockerHubAPI, org, image), 65 | map[string]string{}, 66 | map[string]string{}, 67 | ) 68 | 69 | if err != nil { 70 | return tags, err 71 | } 72 | 73 | result, err := d.httpClient.ToString(response) 74 | 75 | if err != nil { 76 | return tags, err 77 | } 78 | 79 | err = tagsResponse.LoadFromJSON([]byte(result)) 80 | 81 | if err != nil { 82 | return tags, err 83 | } 84 | 85 | for _, result := range tagsResponse.Results { 86 | tags = append(tags, result.Name) 87 | } 88 | 89 | return tags, nil 90 | } 91 | -------------------------------------------------------------------------------- /core/service/docker_hub_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package service 6 | 7 | import ( 8 | "context" 9 | "testing" 10 | 11 | "github.com/franela/goblin" 12 | ) 13 | 14 | // TestUnitGetTags test cases 15 | func TestUnitGetTags(t *testing.T) { 16 | g := goblin.Goblin(t) 17 | 18 | g.Describe("#GetTags", func() { 19 | g.It("It should satisfy all provided test cases", func() { 20 | gitHub := NewDockerHub(NewHTTPClient(20)) 21 | tags, err := gitHub.GetTags(context.Background(), "library", "mysql") 22 | g.Assert(err).Equal(nil) 23 | g.Assert(tags[0] != "").Equal(true) 24 | }) 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /core/service/fuzzy_finder.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package service 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "os/exec" 11 | "strings" 12 | ) 13 | 14 | // FuzzyFinder type 15 | type FuzzyFinder struct{} 16 | 17 | // Available validates if fzf installed 18 | func (f *FuzzyFinder) Available() bool { 19 | _, err := exec.LookPath("fzf") 20 | if err != nil { 21 | return false 22 | } 23 | 24 | return true 25 | } 26 | 27 | // Show shows a fzf list 28 | func (f *FuzzyFinder) Show(items []string) (string, error) { 29 | shell := os.Getenv("SHELL") 30 | 31 | if len(shell) == 0 { 32 | shell = "sh" 33 | } 34 | 35 | cmd := exec.Command(shell, "-c", "fzf -m") 36 | cmd.Stderr = os.Stderr 37 | in, err := cmd.StdinPipe() 38 | 39 | if err != nil { 40 | return "", err 41 | } 42 | 43 | go func() { 44 | for _, item := range items { 45 | fmt.Fprintln(in, item) 46 | } 47 | in.Close() 48 | }() 49 | 50 | result, err := cmd.Output() 51 | 52 | if err != nil { 53 | return "", err 54 | } 55 | 56 | filtered := strings.Split(string(result), "\n") 57 | 58 | return filtered[0], nil 59 | } 60 | -------------------------------------------------------------------------------- /core/service/prompt.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package service 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | 11 | "github.com/manifoldco/promptui" 12 | ) 13 | 14 | // Prompt struct 15 | type Prompt struct { 16 | } 17 | 18 | // NotEmpty returns error if input is empty 19 | func NotEmpty(input string) error { 20 | if strings.TrimSpace(input) == "" { 21 | return fmt.Errorf("Input must not be empty") 22 | } 23 | return nil 24 | } 25 | 26 | // Optional optional value 27 | func Optional(_ string) error { 28 | return nil 29 | } 30 | 31 | // IsEmpty if field is empty 32 | func IsEmpty(input string) bool { 33 | if strings.TrimSpace(input) == "" { 34 | return true 35 | } 36 | return false 37 | } 38 | 39 | // Input request a value from end user 40 | func (p *Prompt) Input(label string, validate promptui.ValidateFunc) (string, error) { 41 | 42 | templates := &promptui.PromptTemplates{ 43 | Prompt: "{{ . }} ", 44 | Valid: "{{ . | green }} ", 45 | Invalid: "{{ . | red }} ", 46 | Success: "{{ . | bold }} ", 47 | } 48 | 49 | item := promptui.Prompt{ 50 | Label: label, 51 | Templates: templates, 52 | Validate: validate, 53 | } 54 | 55 | result, err := item.Run() 56 | 57 | if err != nil { 58 | return "", err 59 | } 60 | 61 | return result, nil 62 | } 63 | 64 | // Select request a value from a list from end user 65 | func (p *Prompt) Select(label string, items []string) (string, error) { 66 | 67 | item := promptui.Select{ 68 | Label: label, 69 | Items: items, 70 | } 71 | 72 | _, result, err := item.Run() 73 | 74 | if err != nil { 75 | return "", err 76 | } 77 | 78 | return result, nil 79 | } 80 | -------------------------------------------------------------------------------- /core/util/helpers_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package util 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | 11 | "github.com/clivern/peanut/pkg" 12 | 13 | "github.com/franela/goblin" 14 | ) 15 | 16 | // TestUnitHelpers 17 | func TestUnitHelpers(t *testing.T) { 18 | baseDir := pkg.GetBaseDir("cache") 19 | pkg.LoadConfigs(fmt.Sprintf("%s/config.dist.yml", baseDir)) 20 | 21 | g := goblin.Goblin(t) 22 | 23 | g.Describe("#TestInArrayFunc", func() { 24 | g.It("It should satisfy test cases", func() { 25 | g.Assert(InArray("A", []string{"A", "B", "C", "D"})).Equal(true) 26 | g.Assert(InArray("B", []string{"A", "B", "C", "D"})).Equal(true) 27 | g.Assert(InArray("H", []string{"A", "B", "C", "D"})).Equal(false) 28 | g.Assert(InArray(1, []int{2, 3, 1})).Equal(true) 29 | g.Assert(InArray(9, []int{2, 3, 1})).Equal(false) 30 | }) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /core/util/map.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package util 6 | 7 | import ( 8 | "sync" 9 | ) 10 | 11 | // Map type 12 | type Map struct { 13 | sync.RWMutex 14 | items map[string]interface{} 15 | } 16 | 17 | // NewMap creates a new instance of Map 18 | func NewMap() Map { 19 | return Map{items: make(map[string]interface{})} 20 | } 21 | 22 | // Get a key from a concurrent map 23 | func (cm *Map) Get(key string) (interface{}, bool) { 24 | cm.Lock() 25 | defer cm.Unlock() 26 | 27 | value, ok := cm.items[key] 28 | 29 | return value, ok 30 | } 31 | 32 | // Set a key in a concurrent map 33 | func (cm *Map) Set(key string, value interface{}) { 34 | cm.Lock() 35 | defer cm.Unlock() 36 | 37 | cm.items[key] = value 38 | } 39 | 40 | // Delete deletes a key 41 | func (cm *Map) Delete(key string) { 42 | cm.Lock() 43 | defer cm.Unlock() 44 | 45 | delete(cm.items, key) 46 | } 47 | -------------------------------------------------------------------------------- /core/util/map_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package util 6 | 7 | import ( 8 | "fmt" 9 | "sync" 10 | "testing" 11 | "time" 12 | 13 | "github.com/franela/goblin" 14 | ) 15 | 16 | // TestUnitMap 17 | func TesUnittMap(t *testing.T) { 18 | g := goblin.Goblin(t) 19 | 20 | g.Describe("#TestMap", func() { 21 | g.It("It should not panic and length is right", func() { 22 | 23 | var wg sync.WaitGroup 24 | cm := &Map{items: make(map[string]interface{})} 25 | 26 | for i := 0; i < 1000; i++ { 27 | wg.Add(1) 28 | go func(wg *sync.WaitGroup, cm *Map, k int) { 29 | cm.Set(fmt.Sprintf("record_%d", k), fmt.Sprintf("value_%d", k)) 30 | cm.Get(fmt.Sprintf("record_%d", k)) 31 | defer wg.Done() 32 | }(&wg, cm, i) 33 | } 34 | // Wait till all above go routines finish 35 | time.Sleep(4 * time.Second) 36 | 37 | for i := 0; i < 500; i++ { 38 | wg.Add(1) 39 | go func(wg *sync.WaitGroup, cm *Map, k int) { 40 | cm.Delete(fmt.Sprintf("record_%d", k)) 41 | defer wg.Done() 42 | }(&wg, cm, i) 43 | } 44 | 45 | wg.Wait() 46 | 47 | g.Assert(len(cm.items)).Equal(500) 48 | }) 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /core/util/shell.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package util 6 | 7 | import ( 8 | "bytes" 9 | "os" 10 | "os/exec" 11 | "strings" 12 | ) 13 | 14 | // Exec run a shell command 15 | func Exec(command string) (string, string, error) { 16 | var outb, errb bytes.Buffer 17 | 18 | items := strings.Split( 19 | command, 20 | " ", 21 | ) 22 | 23 | _, err := exec.LookPath(items[0]) 24 | 25 | if err != nil { 26 | return outb.String(), errb.String(), err 27 | } 28 | 29 | commands := strings.Split( 30 | command, 31 | " ", 32 | ) 33 | 34 | cmd := exec.Command(commands[0], commands[1:]...) 35 | 36 | cmd.Stdin = os.Stdin 37 | cmd.Stdout = &outb 38 | cmd.Stderr = &errb 39 | 40 | err = cmd.Run() 41 | 42 | if err != nil { 43 | return outb.String(), errb.String(), err 44 | } 45 | 46 | return outb.String(), errb.String(), nil 47 | } 48 | -------------------------------------------------------------------------------- /core/util/shell_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package util 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | 11 | "github.com/clivern/peanut/pkg" 12 | 13 | "github.com/franela/goblin" 14 | ) 15 | 16 | // TestUnitShell 17 | func TestUnitShell(t *testing.T) { 18 | g := goblin.Goblin(t) 19 | 20 | baseDir := pkg.GetBaseDir("cache") 21 | 22 | g.Describe("#TestExecFunc", func() { 23 | g.It("It should satisfy test cases", func() { 24 | stout, sterr, err := Exec("echo Hello World") 25 | 26 | g.Assert(err).Equal(nil) 27 | g.Assert(stout).Equal("Hello World\n") 28 | g.Assert(sterr).Equal("") 29 | 30 | stout, sterr, err = Exec("lo Hello World") 31 | 32 | g.Assert(stout).Equal("") 33 | g.Assert(sterr).Equal("") 34 | g.Assert(err.Error()).Equal(`exec: "lo": executable file not found in $PATH`) 35 | 36 | stout, sterr, err = Exec(fmt.Sprintf("bash %s/bin/test_script.sh", baseDir)) 37 | 38 | g.Assert(stout).Equal("Hello World\nExample of error with line number and message\n") 39 | g.Assert(sterr).Equal("test_script.sh: 13: An error has occurred.\n") 40 | g.Assert(err.Error()).Equal("exit status 1") 41 | }) 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /definition/README.md: -------------------------------------------------------------------------------- 1 | These yaml files used to get notified by the latest docker images version -------------------------------------------------------------------------------- /definition/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | elasticsearch: 5 | image: 'elasticsearch:8.17.2' 6 | environment: 7 | - discovery.type=single-node 8 | ports: 9 | - '9200' 10 | - '9300' 11 | restart: unless-stopped 12 | 13 | etcd: 14 | image: 'bitnami/etcd:3.5.14' 15 | environment: 16 | - ALLOW_NONE_AUTHENTICATION=yes 17 | ports: 18 | - '2379' 19 | restart: unless-stopped 20 | 21 | grafana: 22 | image: 'grafana/grafana:11.5.1' 23 | environment: 24 | - 'GF_SECURITY_ADMIN_USER=${ADMIN_USER:-admin}' 25 | - 'GF_SECURITY_ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin}' 26 | - GF_USERS_ALLOW_SIGN_UP=false 27 | - GF_AUTH_ANONYMOUS_ENABLED=true 28 | ports: 29 | - '3000' 30 | restart: unless-stopped 31 | 32 | graphite: 33 | image: 'graphiteapp/graphite-statsd:1.1.7-11' 34 | ports: 35 | - '80' 36 | - 2003-2004 37 | - 2023-2024 38 | - '8125' 39 | - '8126' 40 | restart: unless-stopped 41 | 42 | jaeger: 43 | image: 'jaegertracing/all-in-one:1.60' 44 | ports: 45 | - '6831' 46 | - '16686' 47 | restart: unless-stopped 48 | 49 | mailhog: 50 | image: 'mailhog/mailhog:v1.0.1' 51 | ports: 52 | - '1025' 53 | - '8025' 54 | 55 | mariadb: 56 | image: 'mariadb:10.11.8' 57 | ports: 58 | - 3306 59 | environment: 60 | - MYSQL_ROOT_PASSWORD=root 61 | - MYSQL_DATABASE=peanut 62 | - MYSQL_USER=peanut 63 | - MYSQL_PASSWORD=secret 64 | - MYSQL_ALLOW_EMPTY_PASSWORD=no 65 | restart: unless-stopped 66 | 67 | memcached: 68 | image: 'memcached:1.6.29' 69 | ports: 70 | - '11211' 71 | restart: unless-stopped 72 | 73 | minio: 74 | image: 'minio/minio:RELEASE.2021-06-17T00-10-46Z-28-gac7697426' 75 | command: 'server /data --console-address ":9001"' 76 | environment: 77 | - MINIO_ROOT_USER=peanut 78 | - MINIO_ROOT_PASSWORD=secret 79 | ports: 80 | - '9000' 81 | - '9001' 82 | restart: unless-stopped 83 | 84 | mongodb: 85 | image: 'mongo:8.0.0-rc7' 86 | environment: 87 | - MONGO_INITDB_ROOT_USERNAME=root 88 | - MONGO_INITDB_ROOT_PASSWORD=root 89 | - MONGO_INITDB_DATABASE=peanut 90 | ports: 91 | - '27017' 92 | restart: unless-stopped 93 | 94 | mysql: 95 | image: 'mysql:9.2' 96 | ports: 97 | - 3306 98 | environment: 99 | - MYSQL_ROOT_PASSWORD=root 100 | - MYSQL_DATABASE=peanut 101 | - MYSQL_USER=peanut 102 | - MYSQL_PASSWORD=secret 103 | - MYSQL_ALLOW_EMPTY_PASSWORD=no 104 | restart: unless-stopped 105 | 106 | postgres: 107 | image: 'postgres:16.3' 108 | ports: 109 | - 5432 110 | environment: 111 | - POSTGRES_USER=dbuser 112 | - POSTGRES_PASSWORD=dbpass 113 | - POSTGRES_DB=dbname 114 | restart: unless-stopped 115 | 116 | prometheus: 117 | image: 'prom/prometheus:v2.53.0' 118 | volumes: 119 | - '/root/prometheus.yml:/etc/prometheus/prometheus.yml' 120 | ports: 121 | - '9090' 122 | restart: unless-stopped 123 | 124 | rabbitmq: 125 | image: 'rabbitmq:3.13-management' 126 | ports: 127 | - '5672' 128 | - '15672' 129 | restart: unless-stopped 130 | 131 | redis: 132 | image: 'bitnami/redis:7.4.2' 133 | ports: 134 | - 6379 135 | environment: 136 | - ALLOW_EMPTY_PASSWORD=yes 137 | restart: unless-stopped 138 | 139 | zipkin: 140 | image: 'openzipkin/zipkin:3.5' 141 | ports: 142 | - '9411' 143 | restart: unless-stopped 144 | 145 | vault: 146 | image: 'vault:1.13.2' 147 | ports: 148 | - '8200' 149 | command: vault server -dev -dev-root-token-id=peanut 150 | environment: 151 | - VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:8200 152 | 153 | consul: 154 | image: 'consul:1.15.2' 155 | ports: 156 | - '8500' 157 | command: consul agent -dev -client=0.0.0.0 158 | 159 | minio: 160 | image: 'minio/minio:latest' 161 | command: server /data --console-address :9001 162 | environment: 163 | - MINIO_ROOT_USER=admin1234567 164 | - MINIO_ROOT_PASSWORD=admin1234567 165 | ports: 166 | - '9000' 167 | - '9001' 168 | restart: unless-stopped 169 | 170 | cassandra: 171 | image: cassandra:5.0 172 | ports: 173 | - "9042" 174 | restart: unless-stopped 175 | 176 | registry: 177 | image: registry:2 178 | ports: 179 | - "5000" 180 | restart: unless-stopped 181 | -------------------------------------------------------------------------------- /deployment/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clivern/Peanut/127b073c1cdbea82e005bf9e722491a1894f2cba/deployment/.gitkeep -------------------------------------------------------------------------------- /deployment/linux/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function docker { 4 | echo "Installing docker ..." 5 | apt-get update 6 | apt-get install docker.io -y 7 | systemctl enable docker 8 | echo "docker installation done!" 9 | } 10 | 11 | function docker_compose { 12 | echo "Installing docker-compose ..." 13 | apt-get install docker-compose -y 14 | echo "docker-compose installation done!" 15 | } 16 | 17 | function etcd { 18 | echo "Installing etcd ..." 19 | 20 | ETCD_VER=v3.5.4 21 | 22 | GOOGLE_URL=https://storage.googleapis.com/etcd 23 | GITHUB_URL=https://github.com/etcd-io/etcd/releases/download 24 | DOWNLOAD_URL=${GOOGLE_URL} 25 | 26 | rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz 27 | rm -rf /tmp/etcd-download-test && mkdir -p /tmp/etcd-download-test 28 | 29 | curl -L ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz -o /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz 30 | tar xzvf /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz -C /tmp/etcd-download-test --strip-components=1 31 | rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz 32 | 33 | /tmp/etcd-download-test/etcd --version 34 | /tmp/etcd-download-test/etcdctl version 35 | 36 | cp /tmp/etcd-download-test/etcd /usr/local/bin/ 37 | cp /tmp/etcd-download-test/etcdctl /usr/local/bin/ 38 | 39 | mkdir -p /var/lib/etcd/ 40 | mkdir /etc/etcd 41 | 42 | groupadd --system etcd 43 | useradd -s /sbin/nologin --system -g etcd etcd 44 | 45 | chown -R etcd:etcd /var/lib/etcd/ 46 | 47 | echo "[Unit] 48 | Description=Etcd KV Store 49 | Documentation=https://github.com/etcd-io/etcd 50 | After=network.target 51 | 52 | [Service] 53 | User=etcd 54 | Type=notify 55 | Environment=ETCD_DATA_DIR=/var/lib/etcd 56 | Environment=ETCD_NAME=%m 57 | ExecStart=/usr/local/bin/etcd --listen-client-urls http://127.0.0.1:2379 --advertise-client-urls http://127.0.0.1:2379 58 | Restart=always 59 | RestartSec=10s 60 | LimitNOFILE=40000 61 | 62 | [Install] 63 | WantedBy=multi-user.target" > /etc/systemd/system/etcd.service 64 | 65 | systemctl daemon-reload 66 | systemctl enable etcd.service 67 | systemctl start etcd.service 68 | 69 | echo "etcd installation done!" 70 | } 71 | 72 | function peanut { 73 | echo "Installing peanut ..." 74 | 75 | apt-get install jq -y 76 | 77 | mkdir -p /etc/peanut/storage 78 | 79 | cd /etc/peanut 80 | 81 | PEANUT_LATEST_VERSION=$(curl --silent "https://api.github.com/repos/Clivern/Peanut/releases/latest" | jq '.tag_name' | sed -E 's/.*"([^"]+)".*/\1/' | tr -d v) 82 | 83 | curl -sL https://github.com/Clivern/Peanut/releases/download/v{$PEANUT_LATEST_VERSION}/peanut_{$PEANUT_LATEST_VERSION}_Linux_x86_64.tar.gz | tar xz 84 | 85 | 86 | echo "[Unit] 87 | Description=Peanut 88 | Documentation=https://github.com/clivern/peanut 89 | 90 | [Service] 91 | ExecStart=/etc/peanut/peanut api -c /etc/peanut/config.prod.yml 92 | Restart=on-failure 93 | RestartSec=2 94 | 95 | [Install] 96 | WantedBy=multi-user.target" > /etc/systemd/system/peanut.service 97 | 98 | systemctl daemon-reload 99 | systemctl enable peanut.service 100 | systemctl start peanut.service 101 | 102 | echo "peanut installation done!" 103 | } 104 | 105 | docker 106 | docker_compose 107 | etcd 108 | peanut 109 | -------------------------------------------------------------------------------- /deployment/linux/upgrade.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function peanut { 4 | echo "Upgrade peanut ..." 5 | 6 | cd /etc/peanut 7 | mv config.prod.yml config.back.yml 8 | 9 | PEANUT_LATEST_VERSION=$(curl --silent "https://api.github.com/repos/Clivern/Peanut/releases/latest" | jq '.tag_name' | sed -E 's/.*"([^"]+)".*/\1/' | tr -d v) 10 | 11 | curl -sL https://github.com/Clivern/Peanut/releases/download/v{$PEANUT_LATEST_VERSION}/peanut_{$PEANUT_LATEST_VERSION}_Linux_x86_64.tar.gz | tar xz 12 | 13 | rm config.prod.yml 14 | mv config.back.yml config.prod.yml 15 | 16 | systemctl restart peanut 17 | 18 | echo "peanut upgrade done!" 19 | } 20 | 21 | peanut 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/clivern/peanut 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/drone/envsubst v1.0.3 7 | github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf 8 | github.com/gin-gonic/gin v1.10.0 9 | github.com/manifoldco/promptui v0.9.0 10 | github.com/prometheus/client_golang v1.18.0 11 | github.com/satori/go.uuid v1.2.0 12 | github.com/sirupsen/logrus v1.9.3 13 | github.com/spf13/cobra v1.9.1 14 | github.com/spf13/viper v1.16.0 15 | github.com/stretchr/testify v1.9.0 16 | go.etcd.io/etcd/client/v3 v3.5.9 17 | gopkg.in/yaml.v2 v2.4.0 18 | ) 19 | 20 | require ( 21 | github.com/beorn7/perks v1.0.1 // indirect 22 | github.com/bytedance/sonic v1.11.6 // indirect 23 | github.com/bytedance/sonic/loader v0.1.1 // indirect 24 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 25 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 26 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect 27 | github.com/cloudwego/base64x v0.1.4 // indirect 28 | github.com/cloudwego/iasm v0.2.0 // indirect 29 | github.com/coreos/go-semver v0.3.0 // indirect 30 | github.com/coreos/go-systemd/v22 v22.3.2 // indirect 31 | github.com/davecgh/go-spew v1.1.1 // indirect 32 | github.com/fsnotify/fsnotify v1.6.0 // indirect 33 | github.com/gabriel-vasile/mimetype v1.4.3 // indirect 34 | github.com/gin-contrib/sse v0.1.0 // indirect 35 | github.com/go-playground/locales v0.14.1 // indirect 36 | github.com/go-playground/universal-translator v0.18.1 // indirect 37 | github.com/go-playground/validator/v10 v10.20.0 // indirect 38 | github.com/goccy/go-json v0.10.2 // indirect 39 | github.com/gogo/protobuf v1.3.2 // indirect 40 | github.com/golang/protobuf v1.5.3 // indirect 41 | github.com/hashicorp/hcl v1.0.0 // indirect 42 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 43 | github.com/json-iterator/go v1.1.12 // indirect 44 | github.com/klauspost/cpuid/v2 v2.2.7 // indirect 45 | github.com/leodido/go-urn v1.4.0 // indirect 46 | github.com/magiconair/properties v1.8.7 // indirect 47 | github.com/mattn/go-isatty v0.0.20 // indirect 48 | github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect 49 | github.com/mitchellh/mapstructure v1.5.0 // indirect 50 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 51 | github.com/modern-go/reflect2 v1.0.2 // indirect 52 | github.com/pelletier/go-toml/v2 v2.2.2 // indirect 53 | github.com/pmezard/go-difflib v1.0.0 // indirect 54 | github.com/prometheus/client_model v0.5.0 // indirect 55 | github.com/prometheus/common v0.45.0 // indirect 56 | github.com/prometheus/procfs v0.12.0 // indirect 57 | github.com/spf13/afero v1.9.5 // indirect 58 | github.com/spf13/cast v1.5.1 // indirect 59 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 60 | github.com/spf13/pflag v1.0.6 // indirect 61 | github.com/stretchr/objx v0.5.2 // indirect 62 | github.com/subosito/gotenv v1.4.2 // indirect 63 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 64 | github.com/ugorji/go/codec v1.2.12 // indirect 65 | go.etcd.io/etcd/api/v3 v3.5.9 // indirect 66 | go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect 67 | go.uber.org/atomic v1.9.0 // indirect 68 | go.uber.org/multierr v1.8.0 // indirect 69 | go.uber.org/zap v1.21.0 // indirect 70 | golang.org/x/arch v0.8.0 // indirect 71 | golang.org/x/crypto v0.23.0 // indirect 72 | golang.org/x/net v0.25.0 // indirect 73 | golang.org/x/sys v0.20.0 // indirect 74 | golang.org/x/text v0.15.0 // indirect 75 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect 76 | google.golang.org/grpc v1.56.3 // indirect 77 | google.golang.org/protobuf v1.34.1 // indirect 78 | gopkg.in/ini.v1 v1.67.0 // indirect 79 | gopkg.in/yaml.v3 v3.0.1 // indirect 80 | ) 81 | -------------------------------------------------------------------------------- /peanut.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "embed" 9 | 10 | "github.com/clivern/peanut/cmd" 11 | ) 12 | 13 | var ( 14 | version = "dev" 15 | commit = "none" 16 | date = "unknown" 17 | builtBy = "unknown" 18 | ) 19 | 20 | //go:embed web/dist/* 21 | var static embed.FS 22 | 23 | func main() { 24 | cmd.Version = version 25 | cmd.Commit = commit 26 | cmd.Date = date 27 | cmd.BuiltBy = builtBy 28 | cmd.Static = static 29 | 30 | cmd.Execute() 31 | } 32 | -------------------------------------------------------------------------------- /pkg/loader.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package pkg 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io/ioutil" 11 | "os" 12 | "path/filepath" 13 | 14 | "github.com/drone/envsubst" 15 | "github.com/spf13/viper" 16 | ) 17 | 18 | // GetBaseDir returns the project base dir 19 | // Base dir identified if dirName found 20 | // This function for testing purposes only 21 | func GetBaseDir(dirName string) string { 22 | baseDir, _ := os.Getwd() 23 | cacheDir := fmt.Sprintf("%s/%s", baseDir, dirName) 24 | 25 | for { 26 | if fi, err := os.Stat(cacheDir); err == nil { 27 | if fi.Mode().IsDir() { 28 | return baseDir 29 | } 30 | } 31 | baseDir = filepath.Dir(baseDir) 32 | cacheDir = fmt.Sprintf("%s/%s", baseDir, dirName) 33 | } 34 | } 35 | 36 | // LoadConfigs load configs for testing purposes using viper 37 | func LoadConfigs(path string) error { 38 | data, err := ioutil.ReadFile(path) 39 | 40 | if err != nil { 41 | return err 42 | } 43 | 44 | data1, err := envsubst.EvalEnv(string(data)) 45 | 46 | if err != nil { 47 | return err 48 | } 49 | 50 | viper.SetConfigType("yaml") 51 | 52 | viper.ReadConfig(bytes.NewBufferString(data1)) 53 | 54 | viper.SetDefault("app.name", "x-x-x-x") 55 | 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /pkg/server_mock.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package pkg 6 | 7 | import ( 8 | "net/http" 9 | "net/http/httptest" 10 | ) 11 | 12 | // ServerMock mocks http server 13 | func ServerMock(uri, response string, statusCode int) *httptest.Server { 14 | handler := http.NewServeMux() 15 | handler.HandleFunc(uri, func(w http.ResponseWriter, r *http.Request) { 16 | w.WriteHeader(statusCode) 17 | w.Write([]byte(response)) 18 | }) 19 | 20 | srv := httptest.NewServer(handler) 21 | 22 | return srv 23 | } 24 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /sdk/job.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package sdk 6 | -------------------------------------------------------------------------------- /sdk/service.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package sdk 6 | -------------------------------------------------------------------------------- /static/chart.drawio: -------------------------------------------------------------------------------- 1 | 7Vvbdps4FP0aP8YLEGB4TOykzTRp0zq9zUuWbBRbE0AURGL36+eIi40kfMnUdtxpslYCHAkhtPe5onRQP5q9SXEyvWYBCTuWEcw6aNCxLNM0XDgIybyU9FCvFExSGlSdloIh/UkqoVFJcxqQTOrIGQs5TWThmMUxGXNJhtOUPcnd7lkoPzXBE6IJhmMc6tKvNODTUuo5xlL+ltDJlC9euGqJcN25EmRTHLCnhgidd1A/ZYyXZ9GsT0KxePW6lPddrGhdTCwlMd/mhnw49L9cX4Yfn4L+3Q9+Gzxc5Ce2U02Oz+s3JgEsQHXJUj5lExbj8HwpPUtZHgdEDGvA1bLPFWMJCE0Q/kM4n1do4pwzEE15FFat9zQM+yxkafFEFDjEC2yQZzxlD6TR4lkj5LrQAu+Yzr+JJ3ad+vJ7NYHiYjCTrubVVYCzaTFT8Vh9xapFFK/cEFTr94awiMBg0CElIeb0UeYErqg1WfRb3HrDKDzCMio1cGq6VErgeoY8BMfphPDqriaGykAukgdCtjJQxvJ0TLSB4KTxPktRQZFn0KViyyMO82ot3rKMaxS6ZzGvKSQW/pGknIJOnYZ0EoOMC56c4epqDHCQVMCf4DGNJ7cFi04shSgxi3XyPU0pJ0O4TzzqCUyQTDSNUIbhedbZoqVWaVOlCpgTjmksplVdhyFOMjoq3qng2SzBcT2NlIzzNAOCfCJZSXpjHd/EepDZWsbVgNsy4FavGuJpaYzMus+0aYiQsZqlEi2ezQGkkeCG4DgXND29uexYbghzOBulcDYRZ0OSwgtrJIEV4DJaGiFU3kQ0CEoLJJYZL8FIBOGLF3XOOs5AjAVGp4KisDgNRho6LmvJvjVYjiNhVSt9AyqvBaldAHXa/9Cbue+/fgvod5wYZ7777qFFWfsLVmsgwRFHQnviUSYOn/KY04gcE2qt6ix+Ntv53eDryvguXH0D4N4hAa5jpAbCGl44S8qw6J7OxAKdJSQFXAukBvAMiKPIzVK0FtgCjgoda6VZF70aECHXNm1fgFfHPxJe4oJGRRBWHwc0msB6hHQEf8d5lId5tjy7C2mcz+4oWOhu9jhZy4oW97EvbpiyX/Y1ZjgtzHD2ZaNNU2PGq6M+qKO2PevFHbX9hzhq89loHZmndl899Y4B7snaeEhX3Z5nWxrE53wcgKQPPo23aN2rad6Vaba9Fdn3QUzz46X5MGGIvL+fR5Ph55/hu7+vT3T3rOHfQEAKlgq0MhktpZ5ieT3n3GqD7r740RT6zOgaBpjEvtF1kTgIHbf6RYNZiC1F6rdKiyHUnv6KgXvF3dDeMoipyCxP6VvYIIgoE7EK0Wwiio/dh3xE0hgC2awrwkPxmullcTIghbJtZWwWqro9wWRvUg+wIfizvX3ZGt3vD0gSMjFjDL+fSEAzMUiccRwDj0RZFAyQIfIJ4xrCa1jBl3UnKeOY0wI5V0k81hbytscMmbJRcKyehtqiCN2Ezdqbi/A12D4RLFzEwuOXf0HyNQUjcDQQnfi7stS+AoqvW2r/kJg4utu+JSKPMgYpeCqhRYXmDEjESqCUYK1ULKF1ScqCfKynYC8F2q4gUyNpPfM1rT1FWq2uVUfsisHaFSA9kpAlESkq6jpW5/EjTVlctr8kSruApSe7JNs9HCytKY6tLelRfmc6ku9F7s6+F8k8cJyePNDuvhe1ou61mk/IT0B4QxMSQnqgK+JfJH6A4EQEf5bxhvJpPhIli7GwXJVUvwn6hXjUGFcY5263C5Hfb6/NlowicvVYpTWB2UV9sRVXPVTR1vi18Lyp8PzLNUanXvYDlJlbaVDzssGDz5nIJ/i0yDCmRc3ZEDk+vDoYcNEkAqV6t8axBEPenpIM5bu/3WtR3LZK4y4C2nbEdIss5YbX8+HHKzgeQym4gQ9y0GEQQoajI4QOipBuW39PnUKOuSfMLDnnQC0Fl8NqVU2Qti8tfRZFJVh6zHJVhD/GLWPh0cC2o7zQrMtbta9qA6mtvrK3DATpmeEfELTEhP94yZjFVIrvjqFnoshtoYG7Lxro25deaXBwGrQVJA5LA71I/kqDA9DAkl13zz4YDe6dL356F4Ufrvg5fZd8vhhdXJxsYQzGOYTCdUmIxMGp2FUuoA1xltFxZ/3HUt/tIaxVlpTCFDEDh/Q6jW3OZqe5yXm553nFNudOW1mqFauycrRRMXQEt8wxf7HyBQyRCaJWvlYUrPSBLFc2OMZ2JTRAF88b3arQaeWEEVImjIy189J2cJsb+is5P1J3+aulQ+W71qb5LNZF2VG+cl09X9FgqT+clCv4X8uIrVqq72T9X2vpiymfsovRVgtH2yufMpBr7Uf5bOU5xiblQzJ5nU3Kp/R3NymT3N/eoNyWL7tD2z6AMumbzV5UmY5eKRSP5KgJ6tZKoQ5k7scjWa7s+ZwNHgPV+0UW87LXk9y1WxdkpUdCaiXAW690ljL+BqVzkf288dX+G+aPPCWS8Dcotbu2/3OVGi6X/yNYdl/+pyU6/xc= -------------------------------------------------------------------------------- /static/chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clivern/Peanut/127b073c1cdbea82e005bf9e722491a1894f2cba/static/chart.png -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clivern/Peanut/127b073c1cdbea82e005bf9e722491a1894f2cba/static/logo.png -------------------------------------------------------------------------------- /static/screenshot_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clivern/Peanut/127b073c1cdbea82e005bf9e722491a1894f2cba/static/screenshot_01.png -------------------------------------------------------------------------------- /static/screenshot_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clivern/Peanut/127b073c1cdbea82e005bf9e722491a1894f2cba/static/screenshot_02.png -------------------------------------------------------------------------------- /static/screenshot_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clivern/Peanut/127b073c1cdbea82e005bf9e722491a1894f2cba/static/screenshot_03.png -------------------------------------------------------------------------------- /static/screenshot_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clivern/Peanut/127b073c1cdbea82e005bf9e722491a1894f2cba/static/screenshot_04.png -------------------------------------------------------------------------------- /web/.env.dist: -------------------------------------------------------------------------------- 1 | VUE_APP_API_URL= 2 | -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | .env -------------------------------------------------------------------------------- /web/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { "tabWidth": 2, "insertPragma": true, "useTabs": true, "bracketSpacing": true } 2 | -------------------------------------------------------------------------------- /web/babel.config.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | module.exports = { 4 | presets: ["@vue/cli-plugin-babel/preset"], 5 | }; 6 | -------------------------------------------------------------------------------- /web/dist/css/673.a1f88a72.css: -------------------------------------------------------------------------------- 1 | a[data-v-97b2dba4]{color:#42b983} -------------------------------------------------------------------------------- /web/dist/css/app.e9dfbb89.css: -------------------------------------------------------------------------------- 1 | #app{text-align:center;color:#2c3e50}#nav{padding:30px}#nav a{font-weight:700;color:#2c3e50}#nav a.router-link-exact-active{color:#42b983} -------------------------------------------------------------------------------- /web/dist/img/logo.b0d08bb0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clivern/Peanut/127b073c1cdbea82e005bf9e722491a1894f2cba/web/dist/img/logo.b0d08bb0.png -------------------------------------------------------------------------------- /web/dist/index.html: -------------------------------------------------------------------------------- 1 | Peanut
-------------------------------------------------------------------------------- /web/dist/js/469.f08c0560.js: -------------------------------------------------------------------------------- 1 | "use strict";(self["webpackChunkpeanut"]=self["webpackChunkpeanut"]||[]).push([[469],{1469:function(e,s,t){t.r(s),t.d(s,{default:function(){return u}});var a=function(){var e=this,s=e._self._c;return s("div",{staticClass:"columns is-desktop is-centered"},[s("div",{staticClass:"column is-4"},[s("b-field",{attrs:{label:"API Key"}},[s("b-input",{attrs:{placeholder:"76a97318-2560-4451-a90d-5ba63126d055",rounded:""},model:{value:e.form.api_key,callback:function(s){e.$set(e.form,"api_key",s)},expression:"form.api_key"}})],1),s("div",{staticClass:"field"},[s("p",{staticClass:"control"},[s("b-button",{attrs:{type:"submit is-danger is-light",disabled:e.form.button_disabled},on:{click:e.loginEvent}},[e._v("Login")])],1)])],1)])},i=[],o=(t(7658),{name:"LoginPage",data(){return{form:{api_key:"",button_disabled:!1}}},methods:{loginEvent(){this.form.button_disabled=!0,this.$store.dispatch("service/getServicesAction",{apiKey:this.form.api_key}).then((()=>{this.$buefy.toast.open({message:"You logged in successfully",type:"is-success"}),localStorage.setItem("x_api_key",this.form.api_key),this.$router.push("/")}),(e=>{e.response.data.errorMessage?this.$buefy.toast.open({message:e.response.data.errorMessage,type:"is-danger"}):this.$buefy.toast.open({message:"Oops! invalid api key",type:"is-danger"}),this.form.button_disabled=!1}))}},mounted(){this.loading()}}),n=o,r=t(1001),l=(0,r.Z)(n,a,i,!1,null,null,null),u=l.exports}}]); 2 | //# sourceMappingURL=469.f08c0560.js.map -------------------------------------------------------------------------------- /web/dist/js/469.f08c0560.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"js/469.f08c0560.js","mappings":"wJAAA,IAAIA,EAAS,WAAkB,IAAIC,EAAIC,KAAKC,EAAGF,EAAIG,MAAMD,GAAG,OAAOA,EAAG,MAAM,CAACE,YAAY,kCAAkC,CAACF,EAAG,MAAM,CAACE,YAAY,eAAe,CAACF,EAAG,UAAU,CAACG,MAAM,CAAC,MAAQ,YAAY,CAACH,EAAG,UAAU,CAACG,MAAM,CAAC,YAAc,uCAAuC,QAAU,IAAIC,MAAM,CAACC,MAAOP,EAAIQ,KAAKC,QAASC,SAAS,SAAUC,GAAMX,EAAIY,KAAKZ,EAAIQ,KAAM,UAAWG,EAAI,EAAEE,WAAW,mBAAmB,GAAGX,EAAG,MAAM,CAACE,YAAY,SAAS,CAACF,EAAG,IAAI,CAACE,YAAY,WAAW,CAACF,EAAG,WAAW,CAACG,MAAM,CAAC,KAAO,4BAA4B,SAAWL,EAAIQ,KAAKM,iBAAiBC,GAAG,CAAC,MAAQf,EAAIgB,aAAa,CAAChB,EAAIiB,GAAG,YAAY,MAAM,IACvnB,EACIC,EAAkB,GC0BtB,G,QAAA,CACAC,KAAAA,YACAC,OACA,OACAZ,KAAAA,CACAC,QAAAA,GACAK,iBAAAA,GAGA,EACAO,QAAAA,CACAL,aACA,6BACA,YACAM,SAAAA,4BAAAA,CACAC,OAAAA,KAAAA,KAAAA,UAEAC,MACA,KACA,wBACAC,QAAAA,6BACAC,KAAAA,eAEAC,aAAAA,QAAAA,YAAAA,KAAAA,KAAAA,SACA,0BAEAC,IACA,6BACA,wBACAH,QAAAA,EAAAA,SAAAA,KAAAA,aACAC,KAAAA,cAGA,wBACAD,QAAAA,wBACAC,KAAAA,cAGA,+BAGA,GAEAG,UACA,cACA,ICzEqP,I,UCOjPC,GAAY,OACd,EACA/B,EACAmB,GACA,EACA,KACA,KACA,MAIF,EAAeY,EAAiB,O","sources":["webpack://peanut/./src/views/Login.vue","webpack://peanut/src/views/Login.vue","webpack://peanut/./src/views/Login.vue?7d1c","webpack://peanut/./src/views/Login.vue?a6ee"],"sourcesContent":["var render = function render(){var _vm=this,_c=_vm._self._c;return _c('div',{staticClass:\"columns is-desktop is-centered\"},[_c('div',{staticClass:\"column is-4\"},[_c('b-field',{attrs:{\"label\":\"API Key\"}},[_c('b-input',{attrs:{\"placeholder\":\"76a97318-2560-4451-a90d-5ba63126d055\",\"rounded\":\"\"},model:{value:(_vm.form.api_key),callback:function ($$v) {_vm.$set(_vm.form, \"api_key\", $$v)},expression:\"form.api_key\"}})],1),_c('div',{staticClass:\"field\"},[_c('p',{staticClass:\"control\"},[_c('b-button',{attrs:{\"type\":\"submit is-danger is-light\",\"disabled\":_vm.form.button_disabled},on:{\"click\":_vm.loginEvent}},[_vm._v(\"Login\")])],1)])],1)])\n}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[1]!../../node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./Login.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[1]!../../node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./Login.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./Login.vue?vue&type=template&id=6000b703&\"\nimport script from \"./Login.vue?vue&type=script&lang=js&\"\nexport * from \"./Login.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/@vue/vue-loader-v15/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports"],"names":["render","_vm","this","_c","_self","staticClass","attrs","model","value","form","api_key","callback","$$v","$set","expression","button_disabled","on","loginEvent","_v","staticRenderFns","name","data","methods","dispatch","apiKey","then","message","type","localStorage","err","mounted","component"],"sourceRoot":""} -------------------------------------------------------------------------------- /web/dist/js/673.dc1c89fc.js: -------------------------------------------------------------------------------- 1 | "use strict";(self["webpackChunkpeanut"]=self["webpackChunkpeanut"]||[]).push([[673],{8673:function(t,e,r){r.r(e),r.d(e,{default:function(){return u}});var s=function(){var t=this;t._self._c;return t._m(0)},a=[function(){var t=this,e=t._self._c;return e("div",{staticClass:"home"},[e("img",{attrs:{alt:"logo",src:r(6949),width:"300"}}),e("div",{staticClass:"hello"},[e("br"),e("strong",[t._v("Welcome to Peanut")]),e("p",[t._v(" If you have any suggestions, bug reports, or annoyances "),e("br"),t._v("please report them to our "),e("a",{attrs:{href:"https://github.com/clivern/peanut/issues",target:"_blank",rel:"noopener"}},[t._v("issue tracker")]),t._v(". ")]),e("br"),e("br"),e("small",[e("br"),t._v(" Made with "),e("span",{staticClass:"icon has-text-danger"},[e("i",{staticClass:"fas fa-heart"})]),t._v(" by "),e("a",{attrs:{href:"https://github.com/clivern",target:"_blank",rel:"noopener"}},[t._v("Clivern")]),e("br")])])])}],n={name:"HomePage",data(){return{}},methods:{},mounted(){this.$emit("refresh-state")}},o=n,i=r(1001),l=(0,i.Z)(o,s,a,!1,null,"97b2dba4",null),u=l.exports},6949:function(t,e,r){t.exports=r.p+"img/logo.b0d08bb0.png"}}]); 2 | //# sourceMappingURL=673.dc1c89fc.js.map -------------------------------------------------------------------------------- /web/dist/js/673.dc1c89fc.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"js/673.dc1c89fc.js","mappings":"wJAAA,IAAIA,EAAS,WAAkB,IAAIC,EAAIC,KAAQD,EAAIE,MAAMC,GAAG,OAAOH,EAAII,GAAG,EAC1E,EACIC,EAAkB,CAAC,WAAY,IAAIL,EAAIC,KAAKE,EAAGH,EAAIE,MAAMC,GAAG,OAAOA,EAAG,MAAM,CAACG,YAAY,QAAQ,CAACH,EAAG,MAAM,CAACI,MAAM,CAAC,IAAM,OAAO,IAAMC,EAAQ,MAAsB,MAAQ,SAASL,EAAG,MAAM,CAACG,YAAY,SAAS,CAACH,EAAG,MAAMA,EAAG,SAAS,CAACH,EAAIS,GAAG,uBAAuBN,EAAG,IAAI,CAACH,EAAIS,GAAG,6DAA6DN,EAAG,MAAMH,EAAIS,GAAG,8BAA8BN,EAAG,IAAI,CAACI,MAAM,CAAC,KAAO,2CAA2C,OAAS,SAAS,IAAM,aAAa,CAACP,EAAIS,GAAG,mBAAmBT,EAAIS,GAAG,QAAQN,EAAG,MAAMA,EAAG,MAAMA,EAAG,QAAQ,CAACA,EAAG,MAAMH,EAAIS,GAAG,eAAeN,EAAG,OAAO,CAACG,YAAY,wBAAwB,CAACH,EAAG,IAAI,CAACG,YAAY,mBAAmBN,EAAIS,GAAG,QAAQN,EAAG,IAAI,CAACI,MAAM,CAAC,KAAO,6BAA6B,OAAS,SAAS,IAAM,aAAa,CAACP,EAAIS,GAAG,aAAaN,EAAG,WAC1yB,GCqCA,GACAO,KAAAA,WAEAC,OACA,QACA,EAEAC,QAAAA,CAAAA,EAEAC,UACA,2BACA,GCnDoP,I,UCQhPC,GAAY,OACd,EACAf,EACAM,GACA,EACA,KACA,WACA,MAIF,EAAeS,EAAiB,O","sources":["webpack://peanut/./src/views/Home.vue","webpack://peanut/src/views/Home.vue","webpack://peanut/./src/views/Home.vue?65f5","webpack://peanut/./src/views/Home.vue?8611"],"sourcesContent":["var render = function render(){var _vm=this,_c=_vm._self._c;return _vm._m(0)\n}\nvar staticRenderFns = [function (){var _vm=this,_c=_vm._self._c;return _c('div',{staticClass:\"home\"},[_c('img',{attrs:{\"alt\":\"logo\",\"src\":require(\"../assets/logo.png\"),\"width\":\"300\"}}),_c('div',{staticClass:\"hello\"},[_c('br'),_c('strong',[_vm._v(\"Welcome to Peanut\")]),_c('p',[_vm._v(\" If you have any suggestions, bug reports, or annoyances \"),_c('br'),_vm._v(\"please report them to our \"),_c('a',{attrs:{\"href\":\"https://github.com/clivern/peanut/issues\",\"target\":\"_blank\",\"rel\":\"noopener\"}},[_vm._v(\"issue tracker\")]),_vm._v(\". \")]),_c('br'),_c('br'),_c('small',[_c('br'),_vm._v(\" Made with \"),_c('span',{staticClass:\"icon has-text-danger\"},[_c('i',{staticClass:\"fas fa-heart\"})]),_vm._v(\" by \"),_c('a',{attrs:{\"href\":\"https://github.com/clivern\",\"target\":\"_blank\",\"rel\":\"noopener\"}},[_vm._v(\"Clivern\")]),_c('br')])])])\n}]\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n\n\n","import mod from \"-!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[1]!../../node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./Home.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[1]!../../node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./Home.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./Home.vue?vue&type=template&id=97b2dba4&scoped=true&\"\nimport script from \"./Home.vue?vue&type=script&lang=js&\"\nexport * from \"./Home.vue?vue&type=script&lang=js&\"\nimport style0 from \"./Home.vue?vue&type=style&index=0&id=97b2dba4&prod&scoped=true&lang=css&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/@vue/vue-loader-v15/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"97b2dba4\",\n null\n \n)\n\nexport default component.exports"],"names":["render","_vm","this","_self","_c","_m","staticRenderFns","staticClass","attrs","require","_v","name","data","methods","mounted","component"],"sourceRoot":""} -------------------------------------------------------------------------------- /web/dist/js/757.3e4a3a28.js: -------------------------------------------------------------------------------- 1 | "use strict";(self["webpackChunkpeanut"]=self["webpackChunkpeanut"]||[]).push([[757],{5757:function(n,t,u){u.r(t),u.d(t,{default:function(){return c}});var e=function(){var n=this;n._self._c;return n._m(0)},s=[function(){var n=this,t=n._self._c;return t("div",{staticClass:"not_found"},[t("h2",[n._v("404 Page Not Found")])])}],l=u(1001),r={},a=(0,l.Z)(r,e,s,!1,null,null,null),c=a.exports}}]); 2 | //# sourceMappingURL=757.3e4a3a28.js.map -------------------------------------------------------------------------------- /web/dist/js/757.3e4a3a28.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"js/757.3e4a3a28.js","mappings":"wJAAA,IAAIA,EAAS,WAAkB,IAAIC,EAAIC,KAAQD,EAAIE,MAAMC,GAAG,OAAOH,EAAII,GAAG,EAC1E,EACIC,EAAkB,CAAC,WAAY,IAAIL,EAAIC,KAAKE,EAAGH,EAAIE,MAAMC,GAAG,OAAOA,EAAG,MAAM,CAACG,YAAY,aAAa,CAACH,EAAG,KAAK,CAACH,EAAIO,GAAG,yBAC3H,G,UCFIC,EAAS,CAAC,EAKVC,GAAY,OACdD,EACAT,EACAM,GACA,EACA,KACA,KACA,MAIF,EAAeI,EAAiB,O","sources":["webpack://peanut/./src/views/NotFound.vue","webpack://peanut/./src/views/NotFound.vue?9931"],"sourcesContent":["var render = function render(){var _vm=this,_c=_vm._self._c;return _vm._m(0)\n}\nvar staticRenderFns = [function (){var _vm=this,_c=_vm._self._c;return _c('div',{staticClass:\"not_found\"},[_c('h2',[_vm._v(\"404 Page Not Found\")])])\n}]\n\nexport { render, staticRenderFns }","import { render, staticRenderFns } from \"./NotFound.vue?vue&type=template&id=14c9d593&\"\nvar script = {}\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/@vue/vue-loader-v15/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports"],"names":["render","_vm","this","_self","_c","_m","staticRenderFns","staticClass","_v","script","component"],"sourceRoot":""} -------------------------------------------------------------------------------- /web/dist/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clivern/Peanut/127b073c1cdbea82e005bf9e722491a1894f2cba/web/dist/logo.png -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "peanut", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "axios": "1.7.9", 12 | "buefy": "0.9.29", 13 | "core-js": "3.40.0", 14 | "vue": "2.7.16", 15 | "vue-router": "3.6.5", 16 | "vuex": "3.6.2" 17 | }, 18 | "devDependencies": { 19 | "@babel/core": "7.26.9", 20 | "@babel/eslint-parser": "7.26.8", 21 | "@vue/cli-plugin-babel": "5.0.8", 22 | "@vue/cli-plugin-eslint": "5.0.8", 23 | "@vue/cli-service": "5.0.8", 24 | "eslint": "8.57.0", 25 | "eslint-plugin-vue": "9.27.0", 26 | "vue-template-compiler": "2.7.16" 27 | }, 28 | "eslintConfig": { 29 | "root": true, 30 | "env": { 31 | "node": true 32 | }, 33 | "extends": [ 34 | "plugin:vue/essential", 35 | "eslint:recommended" 36 | ], 37 | "parserOptions": { 38 | "parser": "@babel/eslint-parser" 39 | }, 40 | "rules": {} 41 | }, 42 | "browserslist": [ 43 | "> 1%", 44 | "last 2 versions", 45 | "not dead" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /web/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Peanut 11 | 15 | 16 | 17 | 23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /web/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clivern/Peanut/127b073c1cdbea82e005bf9e722491a1894f2cba/web/public/logo.png -------------------------------------------------------------------------------- /web/src/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 38 | 39 | 55 | 56 | 78 | -------------------------------------------------------------------------------- /web/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clivern/Peanut/127b073c1cdbea82e005bf9e722491a1894f2cba/web/src/assets/logo.png -------------------------------------------------------------------------------- /web/src/common/api.service.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import axios from "axios"; 4 | 5 | const ApiService = { 6 | getURL(endpoint) { 7 | let apiURL = ""; 8 | 9 | if (process.env.VUE_APP_API_URL) { 10 | apiURL = process.env.VUE_APP_API_URL.replace(/\/$/, ""); 11 | } 12 | 13 | return apiURL + endpoint; 14 | }, 15 | 16 | getHeaders(apiKey = "") { 17 | if (localStorage.getItem("x_api_key") != null) { 18 | apiKey = localStorage.getItem("x_api_key"); 19 | } 20 | 21 | return { 22 | crossdomain: true, 23 | 24 | headers: { 25 | "X-API-Key": apiKey, 26 | "X-Client-ID": "dashboard", 27 | "X-Requested-With": "XMLHttpRequest", 28 | "Content-Type": "application/json", 29 | }, 30 | }; 31 | }, 32 | 33 | get(endpoint, apiKey = "") { 34 | return axios.get(this.getURL(endpoint), this.getHeaders(apiKey)); 35 | }, 36 | 37 | delete(endpoint, apiKey = "") { 38 | return axios.delete(this.getURL(endpoint), this.getHeaders(apiKey)); 39 | }, 40 | 41 | post(endpoint, data = {}, apiKey = "") { 42 | return axios.post(this.getURL(endpoint), data, this.getHeaders(apiKey)); 43 | }, 44 | 45 | put(endpoint, data = {}, apiKey = "") { 46 | return axios.put(this.getURL(endpoint), data, this.getHeaders(apiKey)); 47 | }, 48 | }; 49 | 50 | export default ApiService; 51 | -------------------------------------------------------------------------------- /web/src/common/service.api.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import ApiService from "./api.service.js"; 4 | 5 | const getServices = (payload) => { 6 | let apiKey = ""; 7 | 8 | if (payload["apiKey"]) { 9 | apiKey = payload["apiKey"]; 10 | } 11 | 12 | return ApiService.get("/api/v1/service", apiKey); 13 | }; 14 | 15 | const getService = (payload) => { 16 | let apiKey = ""; 17 | 18 | if (payload["apiKey"]) { 19 | apiKey = payload["apiKey"]; 20 | } 21 | 22 | return ApiService.get("/api/v1/service/" + payload["serviceId"], apiKey); 23 | }; 24 | 25 | const deleteService = (payload) => { 26 | let apiKey = ""; 27 | 28 | if (payload["apiKey"]) { 29 | apiKey = payload["apiKey"]; 30 | } 31 | 32 | return ApiService.delete("/api/v1/service/" + payload["serviceId"], apiKey); 33 | }; 34 | 35 | const getJob = (payload) => { 36 | let apiKey = ""; 37 | 38 | if (payload["apiKey"]) { 39 | apiKey = payload["apiKey"]; 40 | } 41 | 42 | return ApiService.get( 43 | "/api/v1/job/" + payload["serviceId"] + "/" + payload["jobId"], 44 | apiKey 45 | ); 46 | }; 47 | 48 | const getServiceTags = (payload) => { 49 | let apiKey = ""; 50 | 51 | if (payload["apiKey"]) { 52 | apiKey = payload["apiKey"]; 53 | } 54 | 55 | return ApiService.get( 56 | "/api/v1/tag/" + payload["serviceType"] + "/" + payload["cache"], 57 | apiKey 58 | ); 59 | }; 60 | 61 | const deployService = (payload) => { 62 | let apiKey = ""; 63 | 64 | if (payload["apiKey"]) { 65 | apiKey = payload["apiKey"]; 66 | } 67 | 68 | return ApiService.post("/api/v1/service", payload, apiKey); 69 | }; 70 | 71 | export { 72 | getServices, 73 | getService, 74 | deleteService, 75 | getJob, 76 | deployService, 77 | getServiceTags, 78 | }; 79 | -------------------------------------------------------------------------------- /web/src/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clivern/Peanut/127b073c1cdbea82e005bf9e722491a1894f2cba/web/src/components/.gitkeep -------------------------------------------------------------------------------- /web/src/main.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import Vue from "vue"; 4 | import App from "./App.vue"; 5 | import router from "./router"; 6 | import axios from "axios"; 7 | import Buefy from "buefy"; 8 | import "buefy/dist/buefy.css"; 9 | import Vuex from "vuex"; 10 | import store from "./store"; 11 | 12 | Vue.use(Vuex); 13 | 14 | Vue.use(Buefy, { defaultIconPack: "fas" }); 15 | 16 | Vue.config.productionTip = false; 17 | 18 | Vue.prototype.$http = axios; 19 | 20 | new Vue({ 21 | store: store, 22 | router, 23 | render: (h) => h(App), 24 | }).$mount("#app"); 25 | -------------------------------------------------------------------------------- /web/src/router/index.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import Vue from "vue"; 4 | import VueRouter from "vue-router"; 5 | 6 | Vue.use(VueRouter); 7 | 8 | const routes = [ 9 | { 10 | path: "/", 11 | name: "Home", 12 | component: () => import("../views/Home.vue"), 13 | meta: { 14 | requiresAuth: false, 15 | }, 16 | }, 17 | { 18 | path: "/login", 19 | name: "Login", 20 | component: () => import("../views/Login.vue"), 21 | meta: { 22 | requiresAuth: false, 23 | }, 24 | }, 25 | { 26 | path: "/deploy", 27 | name: "Deploy", 28 | component: () => import("../views/Deploy.vue"), 29 | meta: { 30 | requiresAuth: true, 31 | }, 32 | }, 33 | { 34 | path: "/services", 35 | name: "Services", 36 | component: () => import("../views/Services.vue"), 37 | meta: { 38 | requiresAuth: true, 39 | }, 40 | }, 41 | { 42 | path: "/404", 43 | name: "NotFound", 44 | component: () => import("../views/NotFound.vue"), 45 | }, 46 | { 47 | path: "*", 48 | redirect: "/404", 49 | }, 50 | ]; 51 | 52 | const router = new VueRouter({ 53 | routes, 54 | }); 55 | 56 | // Auth Middleware 57 | router.beforeEach((to, from, next) => { 58 | if (to.matched.some((record) => record.meta.requiresAuth)) { 59 | if (localStorage.getItem("x_api_key") == null) { 60 | next({ 61 | path: "/login", 62 | params: { nextUrl: to.fullPath }, 63 | }); 64 | } 65 | } else if (to.name == "Login") { 66 | localStorage.removeItem("x_api_key"); 67 | } 68 | next(); 69 | }); 70 | 71 | export default router; 72 | -------------------------------------------------------------------------------- /web/src/store/index.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import Vue from "vue"; 4 | import Vuex from "vuex"; 5 | import service from "./service.module"; 6 | 7 | Vue.use(Vuex); 8 | 9 | export default new Vuex.Store({ 10 | modules: { service }, 11 | }); 12 | -------------------------------------------------------------------------------- /web/src/store/service.module.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | export { getServices, getService, deleteService, getJob, deployService }; 4 | 5 | import { 6 | getServices, 7 | getService, 8 | deleteService, 9 | getJob, 10 | deployService, 11 | getServiceTags, 12 | } from "@/common/service.api"; 13 | 14 | const state = () => ({ 15 | getServicesResult: {}, 16 | getServiceResult: {}, 17 | getDeleteServiceResult: {}, 18 | getJobResult: {}, 19 | getDeployServiceResult: {}, 20 | getServiceTagsResult: {}, 21 | }); 22 | 23 | const getters = { 24 | getServicesResult: (state) => { 25 | return state.getServicesResult; 26 | }, 27 | getServiceResult: (state) => { 28 | return state.getServiceResult; 29 | }, 30 | getDeleteServiceResult: (state) => { 31 | return state.getDeleteServiceResult; 32 | }, 33 | getJobResult: (state) => { 34 | return state.getJobResult; 35 | }, 36 | getDeployServiceResult: (state) => { 37 | return state.getDeployServiceResult; 38 | }, 39 | getServiceTagsResult: (state) => { 40 | return state.getServiceTagsResult; 41 | }, 42 | }; 43 | 44 | const actions = { 45 | async getServicesAction(context, payload) { 46 | const result = await getServices(payload); 47 | context.commit("SET_GET_SERVICES_RESULT", result.data); 48 | return result; 49 | }, 50 | async getServiceAction(context, payload) { 51 | const result = await getService(payload); 52 | context.commit("SET_GET_SERVICE_RESULT", result.data); 53 | return result; 54 | }, 55 | async deleteServiceAction(context, payload) { 56 | const result = await deleteService(payload); 57 | context.commit("SET_DELETE_SERVICE_RESULT", result.data); 58 | return result; 59 | }, 60 | async getJobAction(context, payload) { 61 | const result = await getJob(payload); 62 | context.commit("SET_GET_JOB_RESULT", result.data); 63 | return result; 64 | }, 65 | async deployServiceAction(context, payload) { 66 | const result = await deployService(payload); 67 | context.commit("SET_DEPLOY_SERVICE_RESULT", result.data); 68 | return result; 69 | }, 70 | async getServiceTagsAction(context, payload) { 71 | const result = await getServiceTags(payload); 72 | context.commit("SET_SERVICE_TAGS_RESULT", result.data); 73 | return result; 74 | }, 75 | }; 76 | 77 | const mutations = { 78 | SET_GET_SERVICES_RESULT(state, getServicesResult) { 79 | state.getServicesResult = getServicesResult; 80 | }, 81 | SET_GET_SERVICE_RESULT(state, getServiceResult) { 82 | state.getServiceResult = getServiceResult; 83 | }, 84 | SET_DELETE_SERVICE_RESULT(state, getDeleteServiceResult) { 85 | state.getDeleteServiceResult = getDeleteServiceResult; 86 | }, 87 | SET_GET_JOB_RESULT(state, getJobResult) { 88 | state.getJobResult = getJobResult; 89 | }, 90 | SET_DEPLOY_SERVICE_RESULT(state, getDeployServiceResult) { 91 | state.getDeployServiceResult = getDeployServiceResult; 92 | }, 93 | SET_SERVICE_TAGS_RESULT(state, getServiceTagsResult) { 94 | state.getServiceTagsResult = getServiceTagsResult; 95 | }, 96 | }; 97 | 98 | export default { 99 | namespaced: true, 100 | state, 101 | getters, 102 | actions, 103 | mutations, 104 | }; 105 | -------------------------------------------------------------------------------- /web/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32 | 33 | 34 | 39 | 40 | 55 | -------------------------------------------------------------------------------- /web/src/views/Login.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 27 | 28 | 77 | -------------------------------------------------------------------------------- /web/src/views/NotFound.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | --------------------------------------------------------------------------------