├── .editorconfig
├── .github
├── dependabot.yml
└── workflows
│ ├── integrations-app.yaml
│ ├── integrations-docker.yaml
│ ├── main.yml
│ ├── pages.yaml
│ └── release.yaml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── Dockerfile
├── Dockerfile.alpine
├── LICENSE
├── README.md
├── SECURITY.md
├── docker-compose.yaml
├── docs
├── archetypes
│ └── default.md
├── content
│ ├── _index.md
│ ├── about
│ │ └── _index.md
│ ├── docs
│ │ ├── Aerospike
│ │ │ └── _index.md
│ │ ├── ElasticSearch
│ │ │ └── _index.md
│ │ ├── HTTP
│ │ │ └── _index.md
│ │ ├── Installation
│ │ │ └── _index.md
│ │ ├── Kafka
│ │ │ └── _index.md
│ │ ├── Memcached
│ │ │ └── _index.md
│ │ ├── MongoDB
│ │ │ └── _index.md
│ │ ├── MySQL
│ │ │ └── _index.md
│ │ ├── PostgreSQL
│ │ │ └── _index.md
│ │ ├── RabbitMQ
│ │ │ └── _index.md
│ │ ├── Redis
│ │ │ └── _index.md
│ │ ├── TCP
│ │ │ └── _index.md
│ │ └── _index.md
│ └── index.png
├── go.mod
├── go.sum
├── hugo.yaml
├── i18n
│ └── en.yaml
└── static
│ ├── wait4it-dark.png
│ ├── wait4it-dark.svg
│ ├── wait4it.png
│ └── wait4it.svg
├── go.mod
├── go.sum
├── internal
└── banner
│ └── banner.go
├── main.go
└── pkg
├── aerospike
├── aerospike.go
└── structs.go
├── check
├── check-cmd.go
└── check-module-list.go
├── elasticsearch
└── elasticsearch.go
├── http
├── helpers.go
├── http.go
└── structs.go
├── kafka
├── kafka.go
└── structs.go
├── memcached
├── helpers.go
├── memcached.go
└── structs.go
├── model
├── check-context-struct.go
└── check-interface.go
├── mongodb
├── helpers.go
├── mongodb.go
└── structs.go
├── mysql
├── helpers.go
├── mysql.go
└── structs.go
├── postgresql
├── helpers.go
├── postgresql.go
└── structs.go
├── rabbitmq
└── rabbitmq.go
├── redis
├── helpers.go
├── redis.go
└── structs.go
└── tcp
├── helpers.go
├── structs.go
└── tcp.go
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | tab_width = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [{Makefile,go.mod,go.sum,*.go,.gitmodules}]
13 | indent_style = tab
14 | indent_size = 4
15 |
16 | [*.md]
17 | indent_size = 4
18 | trim_trailing_whitespace = false
19 |
20 | eclint_indent_style = unset
21 |
22 | [Dockerfile]
23 | indent_size = 4
--------------------------------------------------------------------------------
/.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://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "gomod" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "daily"
12 |
--------------------------------------------------------------------------------
/.github/workflows/integrations-app.yaml:
--------------------------------------------------------------------------------
1 | name: Integrations tests (App)
2 | on: push
3 |
4 | jobs:
5 | Build:
6 | runs-on: ubuntu-latest
7 | steps:
8 | - uses: actions/checkout@v3
9 | - uses: actions/setup-go@v3
10 | with:
11 | go-version: '1.23'
12 | check-latest: true
13 | cache: true
14 | - run: GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o wait4it
15 | - name: Cache wait4it
16 | uses: actions/cache@v3
17 | with:
18 | path: wait4it
19 | key: wait4it-${{ github.run_id }}
20 |
21 | Redis-TCP:
22 | runs-on: ubuntu-latest
23 | needs: Build
24 | services:
25 | redis:
26 | image: redis
27 | ports:
28 | - 6379:6379
29 | steps:
30 | - name: Retrieve wait4it
31 | uses: actions/cache@v3
32 | with:
33 | path: wait4it
34 | key: wait4it-${{ github.run_id }}
35 | - name: Test Redis
36 | run: ./wait4it -type=redis -p=6379 -t=60 -h=127.0.0.1
37 | - name: Test TCP
38 | run: ./wait4it -type=tcp -h=127.0.0.1 -p=6379 -t=60
39 |
40 | PostgreSQL:
41 | runs-on: ubuntu-latest
42 | needs: Build
43 | services:
44 | postgres:
45 | image: postgres
46 | env:
47 | POSTGRES_PASSWORD: postgres
48 | ports:
49 | - 5432:5432
50 | steps:
51 | - name: Retrieve wait4it
52 | uses: actions/cache@v3
53 | with:
54 | path: wait4it
55 | key: wait4it-${{ github.run_id }}
56 | - name: Test PostgreSQL
57 | run: ./wait4it -type=postgres -h=127.0.0.1 -p=5432 -t=60 -u=postgres -P=postgres -ssl=disable
58 |
59 | MySQL:
60 | runs-on: ubuntu-latest
61 | needs: Build
62 | services:
63 | redis:
64 | image: mysql
65 | env:
66 | MYSQL_ROOT_PASSWORD: secret
67 | MYSQL_DATABASE: app
68 | ports:
69 | - 3306:3306
70 | steps:
71 | - name: Retrieve wait4it
72 | uses: actions/cache@v3
73 | with:
74 | path: wait4it
75 | key: wait4it-${{ github.run_id }}
76 | - name: Test MySQL
77 | run: ./wait4it -type=mysql -h=127.0.0.1 -p=3306 -t=60 -u=root -P=secret -n=app
78 |
79 | HTTP:
80 | runs-on: ubuntu-latest
81 | needs: Build
82 | services:
83 | redis:
84 | image: nginx
85 | ports:
86 | - 80:80
87 | steps:
88 | - name: Retrieve wait4it
89 | uses: actions/cache@v3
90 | with:
91 | path: wait4it
92 | key: wait4it-${{ github.run_id }}
93 | - name: Test HTTP
94 | run: ./wait4it -type=http -h=http://127.0.0.1/ -t=60 -status-code=200 -http-text="Welcome to nginx!"
95 |
96 | MongoDB:
97 | runs-on: ubuntu-latest
98 | needs: Build
99 | services:
100 | redis:
101 | image: mongo
102 | env:
103 | MONGO_INITDB_ROOT_USERNAME: root
104 | MONGO_INITDB_ROOT_PASSWORD: root
105 | ports:
106 | - 27017:27017
107 | steps:
108 | - name: Retrieve wait4it
109 | uses: actions/cache@v3
110 | with:
111 | path: wait4it
112 | key: wait4it-${{ github.run_id }}
113 | - name: Test MongoDB
114 | run: ./wait4it -type=mongo -p=27017 -t=60 -u=root -P=root -h=127.0.0.1
115 |
116 | RabbitMQ:
117 | runs-on: ubuntu-latest
118 | needs: Build
119 | services:
120 | redis:
121 | image: rabbitmq:3-management-alpine
122 | ports:
123 | - 5672:5672
124 | steps:
125 | - name: Retrieve wait4it
126 | uses: actions/cache@v3
127 | with:
128 | path: wait4it
129 | key: wait4it-${{ github.run_id }}
130 | - name: Test RabbitMQ
131 | run: ./wait4it -type=rabbitmq -p=5672 -t=60 -u=guest -P=guest -h=127.0.0.1
132 |
133 | Memcached:
134 | runs-on: ubuntu-latest
135 | needs: Build
136 | services:
137 | redis:
138 | image: docker.io/bitnami/memcached:1
139 | ports:
140 | - 11211:11211
141 | steps:
142 | - name: Retrieve wait4it
143 | uses: actions/cache@v3
144 | with:
145 | path: wait4it
146 | key: wait4it-${{ github.run_id }}
147 | - name: Test Memcached
148 | run: ./wait4it -type=memcached -h=127.0.0.1 -p=11211 -t=60
149 |
150 | ElasticSearch:
151 | runs-on: ubuntu-latest
152 | needs: Build
153 | services:
154 | redis:
155 | image: docker.io/bitnami/elasticsearch:8
156 | ports:
157 | - 9200:9200
158 | steps:
159 | - name: Retrieve wait4it
160 | uses: actions/cache@v3
161 | with:
162 | path: wait4it
163 | key: wait4it-${{ github.run_id }}
164 | - name: Test ElasticSearch
165 | run: ./wait4it -type=elasticsearch -h=http://127.0.0.1 -p=9200 -t=60
166 |
167 | Aerospike:
168 | runs-on: ubuntu-latest
169 | needs: Build
170 | services:
171 | redis:
172 | image: aerospike/aerospike-server-enterprise
173 | ports:
174 | - 3000:3000
175 | steps:
176 | - name: Retrieve wait4it
177 | uses: actions/cache@v3
178 | with:
179 | path: wait4it
180 | key: wait4it-${{ github.run_id }}
181 | - name: Test Aerospike
182 | run: ./wait4it -type=aerospike -h=127.0.0.1 -p=3000 -t=60
183 |
184 | Kafka:
185 | runs-on: ubuntu-latest
186 | needs: Build
187 | services:
188 | kafka:
189 | image: bitnami/kafka:latest
190 | env:
191 | KAFKA_CFG_NODE_ID: 0
192 | KAFKA_CFG_PROCESS_ROLES: controller,broker
193 | KAFKA_CFG_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093
194 | KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
195 | KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: 0@kafka:9093
196 | KAFKA_CFG_CONTROLLER_LISTENER_NAMES: CONTROLLER
197 | ports:
198 | - 9092:9092
199 | steps:
200 | - name: Retrieve wait4it
201 | uses: actions/cache@v3
202 | with:
203 | path: wait4it
204 | key: wait4it-${{ github.run_id }}
205 | - name: Test Kafka
206 | run: ./wait4it -type=kafka -h=127.0.0.1 -p=9092 -t=60
207 |
--------------------------------------------------------------------------------
/.github/workflows/integrations-docker.yaml:
--------------------------------------------------------------------------------
1 | name: Integrations tests (Docker)
2 | on: push
3 |
4 | jobs:
5 | Build:
6 | runs-on: ubuntu-latest
7 | steps:
8 | - name: Set up Docker Buildx
9 | uses: docker/setup-buildx-action@v2
10 | - uses: actions/checkout@v3
11 | - name: Build image
12 | run: docker build . --file Dockerfile --tag wait4it-pipeline/docker:${{ github.run_id }}
13 | - name: Export image as tar
14 | run: docker save -o wait4it.tar wait4it-pipeline/docker:${{ github.run_id }}
15 | - name: Cache wait4it
16 | uses: actions/cache@v3
17 | with:
18 | path: wait4it.tar
19 | key: wait4it-docker-${{ github.run_id }}
20 |
21 | Redis:
22 | runs-on: ubuntu-latest
23 | needs: Build
24 | env:
25 | PIPELINE_IMAGE_VERSION: ${{ github.run_id }}
26 | steps:
27 | - name: Set up Docker Buildx
28 | uses: docker/setup-buildx-action@v2
29 | - uses: actions/checkout@v3
30 | - name: Retrieve wait4it
31 | uses: actions/cache@v3
32 | with:
33 | path: wait4it.tar
34 | key: wait4it-docker-${{ github.run_id }}
35 | - name: Load image into docker
36 | run: docker load --input wait4it.tar
37 | - name: List images
38 | run: docker image ls
39 | - name: Redis Test
40 | run: docker compose run test-redis
41 | - name: TCP Test
42 | run: docker compose run test-tcp
43 |
44 | PostgreSQL:
45 | runs-on: ubuntu-latest
46 | needs: Build
47 | env:
48 | PIPELINE_IMAGE_VERSION: ${{ github.run_id }}
49 | steps:
50 | - name: Set up Docker Buildx
51 | uses: docker/setup-buildx-action@v2
52 | - uses: actions/checkout@v3
53 | - name: Retrieve wait4it
54 | uses: actions/cache@v3
55 | with:
56 | path: wait4it.tar
57 | key: wait4it-docker-${{ github.run_id }}
58 | - name: Load image into docker
59 | run: docker load --input wait4it.tar
60 | - name: List images
61 | run: docker image ls
62 | - name: PostgreSQL Test
63 | run: docker compose run test-postgres
64 |
65 | MySQL:
66 | runs-on: ubuntu-latest
67 | needs: Build
68 | env:
69 | PIPELINE_IMAGE_VERSION: ${{ github.run_id }}
70 | steps:
71 | - name: Set up Docker Buildx
72 | uses: docker/setup-buildx-action@v2
73 | - uses: actions/checkout@v3
74 | - name: Retrieve wait4it
75 | uses: actions/cache@v3
76 | with:
77 | path: wait4it.tar
78 | key: wait4it-docker-${{ github.run_id }}
79 | - name: Load image into docker
80 | run: docker load --input wait4it.tar
81 | - name: List images
82 | run: docker image ls
83 | - name: MySQL Test
84 | run: docker compose run test-mysql
85 |
86 | HTTP:
87 | runs-on: ubuntu-latest
88 | needs: Build
89 | env:
90 | PIPELINE_IMAGE_VERSION: ${{ github.run_id }}
91 | steps:
92 | - name: Set up Docker Buildx
93 | uses: docker/setup-buildx-action@v2
94 | - uses: actions/checkout@v3
95 | - name: Retrieve wait4it
96 | uses: actions/cache@v3
97 | with:
98 | path: wait4it.tar
99 | key: wait4it-docker-${{ github.run_id }}
100 | - name: Load image into docker
101 | run: docker load --input wait4it.tar
102 | - name: List images
103 | run: docker image ls
104 | - name: HTTP Test
105 | run: docker compose run test-http
106 |
107 | MongoDB:
108 | runs-on: ubuntu-latest
109 | needs: Build
110 | env:
111 | PIPELINE_IMAGE_VERSION: ${{ github.run_id }}
112 | steps:
113 | - name: Set up Docker Buildx
114 | uses: docker/setup-buildx-action@v2
115 | - uses: actions/checkout@v3
116 | - name: Retrieve wait4it
117 | uses: actions/cache@v3
118 | with:
119 | path: wait4it.tar
120 | key: wait4it-docker-${{ github.run_id }}
121 | - name: Load image into docker
122 | run: docker load --input wait4it.tar
123 | - name: List images
124 | run: docker image ls
125 | - name: MongoDB Test
126 | run: docker compose run test-mongodb
127 |
128 | RabbitMQ:
129 | runs-on: ubuntu-latest
130 | needs: Build
131 | env:
132 | PIPELINE_IMAGE_VERSION: ${{ github.run_id }}
133 | steps:
134 | - name: Set up Docker Buildx
135 | uses: docker/setup-buildx-action@v2
136 | - uses: actions/checkout@v3
137 | - name: Retrieve wait4it
138 | uses: actions/cache@v3
139 | with:
140 | path: wait4it.tar
141 | key: wait4it-docker-${{ github.run_id }}
142 | - name: Load image into docker
143 | run: docker load --input wait4it.tar
144 | - name: List images
145 | run: docker image ls
146 | - name: RabbitMQ Test
147 | run: docker compose run test-rabbitmq
148 |
149 | Memcached:
150 | runs-on: ubuntu-latest
151 | needs: Build
152 | env:
153 | PIPELINE_IMAGE_VERSION: ${{ github.run_id }}
154 | steps:
155 | - name: Set up Docker Buildx
156 | uses: docker/setup-buildx-action@v2
157 | - uses: actions/checkout@v3
158 | - name: Retrieve wait4it
159 | uses: actions/cache@v3
160 | with:
161 | path: wait4it.tar
162 | key: wait4it-docker-${{ github.run_id }}
163 | - name: Load image into docker
164 | run: docker load --input wait4it.tar
165 | - name: List images
166 | run: docker image ls
167 | - name: Memcached Test
168 | run: docker compose run test-memcached
169 |
170 | ElasticSearch:
171 | runs-on: ubuntu-latest
172 | needs: Build
173 | env:
174 | PIPELINE_IMAGE_VERSION: ${{ github.run_id }}
175 | steps:
176 | - name: Set up Docker Buildx
177 | uses: docker/setup-buildx-action@v2
178 | - uses: actions/checkout@v3
179 | - name: Retrieve wait4it
180 | uses: actions/cache@v3
181 | with:
182 | path: wait4it.tar
183 | key: wait4it-docker-${{ github.run_id }}
184 | - name: Load image into docker
185 | run: docker load --input wait4it.tar
186 | - name: List images
187 | run: docker image ls
188 | - name: ElasticSearch Test
189 | run: docker compose run test-elasticsearch
190 |
191 | Aerospike:
192 | runs-on: ubuntu-latest
193 | needs: Build
194 | env:
195 | PIPELINE_IMAGE_VERSION: ${{ github.run_id }}
196 | steps:
197 | - name: Set up Docker Buildx
198 | uses: docker/setup-buildx-action@v2
199 | - uses: actions/checkout@v3
200 | - name: Retrieve wait4it
201 | uses: actions/cache@v3
202 | with:
203 | path: wait4it.tar
204 | key: wait4it-docker-${{ github.run_id }}
205 | - name: Load image into docker
206 | run: docker load --input wait4it.tar
207 | - name: List images
208 | run: docker image ls
209 | - name: Aerospike Test
210 | run: docker compose run test-aerospike
211 |
212 | Kafka:
213 | runs-on: ubuntu-latest
214 | needs: Build
215 | env:
216 | PIPELINE_IMAGE_VERSION: ${{ github.run_id }}
217 | steps:
218 | - name: Set up Docker Buildx
219 | uses: docker/setup-buildx-action@v2
220 | - uses: actions/checkout@v3
221 | - name: Retrieve wait4it
222 | uses: actions/cache@v3
223 | with:
224 | path: wait4it.tar
225 | key: wait4it-docker-${{ github.run_id }}
226 | - name: Load image into docker
227 | run: docker load --input wait4it.tar
228 | - name: List images
229 | run: docker image ls
230 | - name: Kafka Test
231 | run: docker compose run test-kafka
232 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Dockerhub Release
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | tags:
7 | - v*
8 |
9 | jobs:
10 | build-scratch:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v3
14 | - name: Build docker image
15 | run: |
16 | # Strip git ref prefix from version
17 | VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
18 | docker build . --file Dockerfile --label "org.opencontainers.image.revision=$VERSION" --tag wait4it
19 | - name: Logging into docker hub
20 | run: echo "${{ secrets.DOCKERHUBPWD }}" | docker login --username ph4r5h4d --password-stdin
21 | - name: Tag and push
22 | run: |
23 | IMAGE=ph4r5h4d/wait4it
24 |
25 | # Strip git ref prefix from version
26 | VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
27 | # Strip "v" prefix from tag name
28 | [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
29 |
30 | echo $IMAGE
31 | echo $VERSION
32 |
33 | docker tag wait4it $IMAGE:$VERSION-scratch
34 | docker tag wait4it $IMAGE:scratch
35 |
36 | docker push $IMAGE:$VERSION-scratch
37 | docker push $IMAGE:scratch
38 |
39 | build-alpine:
40 | runs-on: ubuntu-latest
41 | steps:
42 | - uses: actions/checkout@v3
43 | - name: Build docker image
44 | run: |
45 | # Strip git ref prefix from version
46 | VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
47 | docker build . --file Dockerfile.alpine --label "org.opencontainers.image.revision=$VERSION" --tag wait4it
48 | - name: Logging into docker hub
49 | run: echo "${{ secrets.DOCKERHUBPWD }}" | docker login --username ph4r5h4d --password-stdin
50 | - name: Tag and push
51 | run: |
52 | IMAGE=ph4r5h4d/wait4it
53 |
54 | # Strip git ref prefix from version
55 | VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
56 | # Strip "v" prefix from tag name
57 | [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
58 |
59 | echo $IMAGE
60 | echo $VERSION
61 |
62 | docker tag wait4it $IMAGE:$VERSION-alpine
63 | docker tag wait4it $IMAGE:alpine
64 | docker tag wait4it $IMAGE:latest
65 |
66 | docker push $IMAGE:$VERSION-alpine
67 | docker push $IMAGE:alpine
68 | docker push $IMAGE:latest
69 |
--------------------------------------------------------------------------------
/.github/workflows/pages.yaml:
--------------------------------------------------------------------------------
1 | name: Deploy Hugo site to Pages
2 | on:
3 | push:
4 | branches: ["main"]
5 | workflow_dispatch:
6 | permissions:
7 | contents: read
8 | pages: write
9 | id-token: write
10 | concurrency:
11 | group: "pages"
12 | cancel-in-progress: false
13 | defaults:
14 | run:
15 | shell: bash
16 | jobs:
17 | build:
18 | runs-on: ubuntu-latest
19 | env:
20 | HUGO_VERSION: 0.140.1
21 | steps:
22 | - name: Checkout
23 | uses: actions/checkout@v4
24 | with:
25 | fetch-depth: 0
26 | submodules: recursive
27 | - name: Setup Go
28 | uses: actions/setup-go@v5
29 | with:
30 | go-version: '1.23'
31 | - name: Setup Pages
32 | id: pages
33 | uses: actions/configure-pages@v4
34 | - name: Setup Hugo
35 | run: |
36 | wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
37 | && sudo dpkg -i ${{ runner.temp }}/hugo.deb
38 | - name: Build with Hugo
39 | env:
40 | HUGO_ENVIRONMENT: production
41 | HUGO_ENV: production
42 | run: |
43 | hugo \
44 | --gc --minify -s ./docs \
45 | --baseURL "${{ steps.pages.outputs.base_url }}/"
46 | - name: Upload artifact
47 | uses: actions/upload-pages-artifact@v3
48 | with:
49 | path: ./docs/public
50 |
51 | deploy:
52 | environment:
53 | name: github-pages
54 | url: ${{ steps.deployment.outputs.page_url }}
55 | runs-on: ubuntu-latest
56 | needs: build
57 | steps:
58 | - name: Deploy to GitHub Pages
59 | id: deployment
60 | uses: actions/deploy-pages@v4
61 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: Github Release
2 |
3 | on:
4 | workflow_dispatch:
5 | release:
6 | types: [created]
7 |
8 | jobs:
9 | release:
10 | name: release linux/amd64
11 | runs-on: ubuntu-latest
12 | strategy:
13 | matrix:
14 | goos: [ linux, windows, darwin ]
15 | goarch: [ amd64, arm64, arm ]
16 | exclude:
17 | - goarch: "386"
18 | goos: darwin
19 | - goarch: "arm"
20 | goos: darwin
21 | - goarch: arm64
22 | goos: windows
23 | steps:
24 | - uses: actions/checkout@v3
25 | - uses: wangyoucao577/go-release-action@v1
26 | with:
27 | github_token: ${{ secrets.GITHUB_TOKEN }}
28 | goos: ${{ matrix.goos }}
29 | goarch: ${{ matrix.goarch }}
30 | binary_name: wait4it
31 | extra_files: LICENSE README.md
32 | md5sum: true
33 | sha256sum: true
34 | goversion: 1.23
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, built with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | # Dependency directories (remove the comment below to include it)
15 | # vendor/
16 | .idea
17 | docs/public
18 | docs/.hugo_build.lock
19 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | @Ph4r5h4d.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.23-alpine as builder
2 | RUN apk update && apk add --no-cache gcc git
3 |
4 | ENV USER=appuser
5 | ENV UID=10001
6 | RUN adduser \
7 | --disabled-password \
8 | --gecos "" \
9 | --home "/nonexistent" \
10 | --shell "/sbin/nologin" \
11 | --no-create-home \
12 | --uid "${UID}" \
13 | "${USER}"
14 |
15 | WORKDIR $GOPATH/src/github.com/ph4r5h4d/wait4it
16 | COPY . .
17 | RUN go mod download
18 | RUN go mod verify
19 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o /go/bin/wait4it
20 | RUN chown appuser:appuser /go/bin/wait4it
21 |
22 | FROM scratch
23 | LABEL org.opencontainers.image.source="https://github.com/ph4r5h4d/wait4it"
24 | COPY --from=builder /etc/passwd /etc/passwd
25 | COPY --from=builder /etc/group /etc/group
26 | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
27 | COPY --from=builder /go/bin/wait4it /go/bin/wait4it
28 |
29 | USER appuser:appuser
30 | ENTRYPOINT ["/go/bin/wait4it"]
31 |
--------------------------------------------------------------------------------
/Dockerfile.alpine:
--------------------------------------------------------------------------------
1 | FROM golang:1.23-alpine as build-env
2 | RUN apk add git gcc
3 | RUN mkdir /app
4 | WORKDIR /app
5 | COPY . .
6 | RUN go mod download
7 | RUN go mod verify
8 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o wait4it
9 | FROM alpine:3.17
10 | LABEL org.opencontainers.image.source="https://github.com/ph4r5h4d/wait4it"
11 | COPY --from=build-env /app/wait4it .
12 | USER 1001
13 | ENTRYPOINT ["./wait4it"]
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # wait4it
2 |
3 |    [](https://hub.docker.com/r/ph4r5h4d/wait4it)    
4 |
5 | A simple go application to test whether a port is ready to accept a connection or check
6 | MySQL, PostgreSQL, MongoDB or Redis server is ready or not, Also you can do Http call and check
7 | the response code and text in response.
8 | It also supports **timeout** so it can wait for a particular time and then fail.
9 |
10 | ## Supported Services
11 | * [TCP port](https://wait4it.dev/docs/tcp/)
12 | * [MySQL](https://wait4it.dev/docs/mysql/)
13 | * [PostgresQL](https://wait4it.dev/docs/postgresql/)
14 | * [Http](https://wait4it.dev/docs/http/)
15 | * [MongoDB](https://wait4it.dev/docs/mongodb/)
16 | * [Redis](https://wait4it.dev/docs/redis/)
17 | * [RabbitMQ](https://wait4it.dev/docs/rabbitmq/)
18 | * [Memcached](https://wait4it.dev/docs/memcached/)
19 | * [ElasticSearch](https://wait4it.dev/docs/elasticsearch/)
20 | * [Aerospike](https://wait4it.dev/docs/aerospike/)
21 | * [Kafka](https://wait4it.dev/docs/kafka/)
22 |
23 | ## Install
24 | You can download the latest [release](https://github.com/ph4r5h4d/wait4it/releases), or you can build it yourself.
25 | To build just run `go build`.
26 | For detailed installation instructions, visit the [installation doc](https://wait4it.dev/docs/installation/).
27 |
28 | ## Documentation
29 | Visit the [website](https://wait4it.dev) for detailed documentation.
30 |
31 | ## Powered by
32 | [](https://jb.gg/OpenSourceSupport)
33 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | This project doesn't follow semantic versioning, always use the latest build or Docker image.
6 | I tend to actively maintain the project for the time being so patches and updates comes time to time.
7 |
8 | ## Reporting a Vulnerability
9 |
10 | If there is a security concern or you are aware of an issue, please create a issue in the project, I'll try
11 | my best to address it as soon as possible.
12 |
--------------------------------------------------------------------------------
/docker-compose.yaml:
--------------------------------------------------------------------------------
1 | services:
2 | build:
3 | build: .
4 | image: wait4it-pipeline/docker:${PIPELINE_IMAGE_VERSION:-latest}
5 |
6 | redis:
7 | image: redis:latest
8 |
9 | postgres:
10 | image: postgres
11 | environment:
12 | POSTGRES_PASSWORD: postgres
13 |
14 | mysql:
15 | image: mysql
16 | environment:
17 | MYSQL_ROOT_PASSWORD: secret
18 | MYSQL_DATABASE: app
19 |
20 | nginx:
21 | image: nginx
22 |
23 | mongodb:
24 | image: mongo
25 | environment:
26 | MONGO_INITDB_ROOT_USERNAME: root
27 | MONGO_INITDB_ROOT_PASSWORD: root
28 |
29 | rabbitmq:
30 | image: rabbitmq:3-management-alpine
31 |
32 | memcached:
33 | image: docker.io/bitnami/memcached:1
34 |
35 | elasticsearch:
36 | image: docker.io/bitnami/elasticsearch:8
37 |
38 | aerospike:
39 | image: aerospike/aerospike-server-enterprise
40 |
41 | kafka:
42 | image: bitnami/kafka:latest
43 | environment:
44 | - KAFKA_CFG_NODE_ID=0
45 | - KAFKA_CFG_PROCESS_ROLES=controller,broker
46 | - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
47 | - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
48 | - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093
49 | - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
50 |
51 | test-redis:
52 | image: wait4it-pipeline/docker:${PIPELINE_IMAGE_VERSION:-latest}
53 | command: -type=redis -p=6379 -t=60 -h=redis
54 | depends_on:
55 | - build
56 | - redis
57 |
58 | test-tcp:
59 | image: wait4it-pipeline/docker:${PIPELINE_IMAGE_VERSION:-latest}
60 | command: -type=tcp -h=redis -p=6379 -t=60
61 | depends_on:
62 | - build
63 | - redis
64 |
65 | test-postgres:
66 | image: wait4it-pipeline/docker:${PIPELINE_IMAGE_VERSION:-latest}
67 | command: -type=postgres -h=postgres -p=5432 -t=60 -u=postgres -P=postgres -ssl=disable
68 | depends_on:
69 | - build
70 | - postgres
71 |
72 | test-mysql:
73 | image: wait4it-pipeline/docker:${PIPELINE_IMAGE_VERSION:-latest}
74 | command: -type=mysql -h=mysql -p=3306 -t=60 -u=root -P=secret -n=app
75 | depends_on:
76 | - build
77 | - mysql
78 |
79 | test-http:
80 | image: wait4it-pipeline/docker:${PIPELINE_IMAGE_VERSION:-latest}
81 | command: -type=http -h=http://nginx/ -t=60 -status-code=200 -http-text="Welcome to nginx!"
82 | depends_on:
83 | - build
84 | - nginx
85 |
86 | test-mongodb:
87 | image: wait4it-pipeline/docker:${PIPELINE_IMAGE_VERSION:-latest}
88 | command: -type=mongo -p=27017 -t=60 -u=root -P=root -h=mongodb
89 | depends_on:
90 | - build
91 | - mongodb
92 |
93 | test-rabbitmq:
94 | image: wait4it-pipeline/docker:${PIPELINE_IMAGE_VERSION:-latest}
95 | command: -type=rabbitmq -p=5672 -t=60 -u=guest -P=guest -h=rabbitmq
96 | depends_on:
97 | - build
98 | - rabbitmq
99 |
100 | test-memcached:
101 | image: wait4it-pipeline/docker:${PIPELINE_IMAGE_VERSION:-latest}
102 | command: -type=memcached -h=memcached -p=11211 -t=60
103 | depends_on:
104 | - build
105 | - memcached
106 |
107 | test-elasticsearch:
108 | image: wait4it-pipeline/docker:${PIPELINE_IMAGE_VERSION:-latest}
109 | command: -type=elasticsearch -h=http://elasticsearch -p=9200 -t=60
110 | depends_on:
111 | - build
112 | - elasticsearch
113 |
114 | test-aerospike:
115 | image: wait4it-pipeline/docker:${PIPELINE_IMAGE_VERSION:-latest}
116 | command: -type=aerospike -h=aerospike -p=3000 -t=60
117 | depends_on:
118 | - build
119 | - aerospike
120 |
121 | test-kafka:
122 | image: wait4it-pipeline/docker:${PIPELINE_IMAGE_VERSION:-latest}
123 | command: -type=kafka -h=kafka -p=9092 -t=60
124 | depends_on:
125 | - build
126 | - kafka
127 |
--------------------------------------------------------------------------------
/docs/archetypes/default.md:
--------------------------------------------------------------------------------
1 | ---
2 | date: '{{ .Date }}'
3 | draft: true
4 | title: '{{ replace .File.ContentBaseName "-" " " | title }}'
5 | ---
6 |
--------------------------------------------------------------------------------
/docs/content/_index.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2024-12-24T21:25:42+01:00'
3 | draft = false
4 | title = 'Wait4it'
5 | +++
6 | [](https://hub.docker.com/r/ph4r5h4d/wait4it)
7 | 
8 |
9 | Ensure your services are ready to perform with Wait4it—a lightweight tool designed to test service readiness and exit gracefully once the checks are successful.
10 | 
11 |
12 | ## What is Wait4it?
13 |
14 | Wait4it is a simple, powerful command-line tool that:
15 |
16 | - **Waits for Services to Be Ready**: Checks ports, databases, or HTTP endpoints to confirm they’re operational.
17 | - **Exits Upon Success**: Once the target service is ready, Wait4it terminates with an exit code of 0, making it ideal for scripts, pipelines, and startup sequences.
18 | - **Lightweight and Focused**: It’s not an orchestrator; it’s a utility designed to integrate seamlessly into your existing workflows.
19 |
20 | ## Key Features
21 |
22 | - **Port Readiness**: Verify if TCP ports are open and accepting connections.
23 | - **Service Health Checks**: Ensure MySQL, PostgreSQL, MongoDB, Redis, RabbitMQ, Memcached, ElasticSearch, and Aerospike are fully operational.
24 | - **HTTP Monitoring**: Validate HTTP response codes and content.
25 | - **Custom Timeouts**: Configure wait times for services before marking them as unavailable.
26 |
27 | ## A Practical Example:
28 |
29 | Let’s say your application depends on MySQL and needs to perform critical database operations during startup. In a complex Kubernetes environment, MySQL’s port might open quickly, but the database itself could take extra time to initialize.
30 |
31 | Without a readiness tool, your application might attempt to connect prematurely, leading to startup failures.
32 |
33 | **Enter Wait4it**:
34 |
35 | 1. Configure Wait4it to check MySQL’s availability continuously.
36 | 2. It waits until MySQL is fully ready (not just the port being open).
37 | 3. Upon success, Wait4it exits with code 0, allowing your application to proceed with confidence.
38 |
39 | This approach ensures smooth service synchronization while leaving orchestration to your existing tools, like Kubernetes or CI/CD pipelines.
40 |
41 | ## Get Started with Wait4it
42 |
43 | Say goodbye to failed startups and hello to readiness. Whether in development or production, Wait4it is your go-to solution for ensuring service health with minimal effort.
44 |
45 | ### Powered by
46 | [](https://jb.gg/OpenSourceSupport)
47 |
--------------------------------------------------------------------------------
/docs/content/about/_index.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2024-12-28T13:25:42+01:00'
3 | draft = false
4 | title = 'About ME'
5 | +++
6 |
7 | 👋 Hi there! I’m Farshad, a passionate software engineer from the Netherlands. With a strong focus on building scalable, secure, and reliable solutions, I thrive on solving complex problems and turning ideas into robust software systems.
8 |
9 | ## 💻 Expertise
10 | You can check out [my personal website](https://farshad.nematdoust.com) for more information about my work and projects. My areas of expertise include:
11 | - **Software Development**: I specialize in backend development, microservices, and cloud-native applications.
12 | - **DevOps**: I have experience with CI/CD pipelines, containerization, and infrastructure as code.
13 | - **Database Management**: I’m proficient in SQL and NoSQL databases, including MySQL, PostgreSQL, MongoDB, and Redis.
14 | - **System Architecture**: I design scalable and fault-tolerant systems using modern technologies and best practices.
15 | - **Open Source**: I try to contribute to open-source projects and maintain several tools on GitHub.
16 | - **Agile Methodologies**: I’m well-versed in Agile practices and collaborative development workflows.
17 | - **Problem-Solving**: I love tackling challenging problems and finding elegant solutions.
18 | - **Continuous Learning**: I’m always exploring new technologies and expanding my skill set.
19 |
20 | ## 🎯 Goals & Philosophy
21 | I believe in continuous learning and staying on top of emerging trends in technology. My aim is to create impactful software that is both user-friendly and technically sound.
22 |
23 | Feel free to reach out or collaborate! I’m always open to discussing innovative ideas, sharing knowledge, and working on exciting projects.
24 |
25 | ---
26 |
27 | 📫 **Contact Me**
28 | - GitHub: [ph4r5h4d](https://github.com/ph4r5h4d)
29 | - Email: farshad [at] nematdoust [dot] com
30 |
--------------------------------------------------------------------------------
/docs/content/docs/Aerospike/_index.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2024-12-24T21:25:42+01:00'
3 | draft = false
4 | title = 'Aerospike'
5 | +++
6 | Aerospike Check validates connectivity to an Aerospike instance.
7 |
8 | ## Usage with Binary
9 | ```bash
10 | ./wait4it -type=aerospike -h=127.0.0.1 -p=3000 -t=60
11 | ```
12 |
13 | ## Usage with Docker
14 | ```bash
15 | docker run ph4r5h4d/wait4it -type=aerospike -h=127.0.0.1 -p=3000 -t=60
16 | ```
17 |
18 | ## Environment Variables
19 |
20 | | Variable | Description | Default |
21 | |------------------|---------------------------------------------------------------------------|-----------|
22 | | W4IT_TYPE | The type of check (set to `aerospike` for Aerospike check). | - |
23 | | W4IT_TIMEOUT | Timeout in seconds. | 30 |
24 | | W4IT_HOST | The host to check. | 127.0.0.1 |
25 | | W4IT_PORT | The port to check on the Aerospike host. | 3000 |
26 |
27 | ## Command-Line Arguments
28 |
29 | | Argument | Description | Default |
30 | |------------------|---------------------------------------------------------------------------|-----------|
31 | | -type | The type of check (set to `aerospike`). | - |
32 | | -t | Timeout in seconds. | 30 |
33 | | -h | The host to check. | 127.0.0.1 |
34 | | -p | The port to check on the Aerospike host. | 3000 |
35 |
36 | ## Notes
37 | {{< callout type="info" >}}
38 | - Environment variables override command-line arguments.
39 | - Multiple hosts and cluster checks are not supported at the moment.
40 | {{< /callout >}}
41 |
42 | ## Exit Codes
43 | | Code | Meaning |
44 | |------|------------------------------------|
45 | | 0 | Connection successful. |
46 | | 1 | Timed out. |
47 | | 2 | Validation error or incorrect input.
48 |
--------------------------------------------------------------------------------
/docs/content/docs/ElasticSearch/_index.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2024-12-24T21:25:42+01:00'
3 | draft = false
4 | title = 'ElasitcSearch'
5 | +++
6 | ElasticSearch Check validates connectivity to an ElasticSearch instance and optionally checks for username and password authentication (if the X-Pack extension is enabled).
7 |
8 | ## Usage with Binary
9 | ```bash
10 | ./wait4it -type=elasticsearch -h=http://127.0.0.1 -p=9200 -t=60
11 | ```
12 |
13 | ## Usage with Docker
14 | ```bash
15 | docker run ph4r5h4d/wait4it -type=elasticsearch -h=http://127.0.0.1 -p=9200 -t=60
16 | ```
17 |
18 | ## Environment Variables
19 |
20 | | Variable | Description | Default |
21 | |------------------|---------------------------------------------------------------------------|-----------|
22 | | W4IT_TYPE | The type of check (set to `elasticsearch` for ElasticSearch check). | - |
23 | | W4IT_TIMEOUT | Timeout in seconds. | 30 |
24 | | W4IT_HOST | The host to check. | 127.0.0.1 |
25 | | W4IT_PORT | The port to check on the ElasticSearch host. | 9200 |
26 |
27 | ## Command-Line Arguments
28 |
29 | | Argument | Description | Default |
30 | |------------------|---------------------------------------------------------------------------|-----------|
31 | | -type | The type of check (set to `elasticsearch`). | - |
32 | | -t | Timeout in seconds. | 30 |
33 | | -h | The host to check. | 127.0.0.1 |
34 | | -p | The port to check on the ElasticSearch host. | 9200 |
35 |
36 | ## Notes
37 | {{< callout type="info" >}}
38 | - Environment variables override command-line arguments.
39 | - Multiple hosts and cluster checks are not supported at the moment.
40 | - Username/password authentication is only supported when the X-Pack extension is enabled. If X-Pack is not activated, the username and password are ignored.
41 | {{< /callout >}}
42 |
43 | ## Exit Codes
44 | | Code | Meaning |
45 | |------|------------------------------------|
46 | | 0 | Connection successful. |
47 | | 1 | Timed out. |
48 | | 2 | Validation error or incorrect input.
49 |
--------------------------------------------------------------------------------
/docs/content/docs/HTTP/_index.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2024-12-24T21:25:42+01:00'
3 | draft = false
4 | title = 'HTTP Check'
5 | +++
6 | HTTP Check validates connectivity to an HTTP endpoint and optionally checks for specific status codes or text in the response.
7 |
8 | ## Usage with Binary
9 | ```bash
10 | ./wait4it -type=http -h=https://example.com -t=60 -http-status=200 -http-text="Welcome"
11 | ```
12 | ## Usage with Docker
13 | ```bash
14 | docker run ph4r5h4d/wait4it -type=http -h=https://example.com -t=60 -http-status=200 -http-text="Welcome"
15 | ```
16 | ## Environment Variables
17 |
18 | | Variable | Description | Default |
19 | |----------------------------|-------------------------------------------------------------------------------------------------|-----------|
20 | | W4IT_TYPE | The type of check (set to `http` for HTTP check). | - |
21 | | W4IT_TIMEOUT | Timeout in seconds. | 30 |
22 | | W4IT_HOST | The HTTP endpoint to check. | 127.0.0.1 |
23 | | W4IT_HTTP_STATUS_CODE | Expected HTTP status code. | 200 |
24 | | W4IT_HTTP_TEXT | Substring to search for in the HTTP response. | - |
25 | | W4IT_HTTP_FOLLOW_REDIRECT | Whether to follow redirects (`true` or `false`). | true |
26 |
27 | ## Command-Line Arguments
28 |
29 | | Argument | Description | Default |
30 | |----------------------------|-------------------------------------------------------------------------------------------------|-----------|
31 | | -type | The type of check (set to `http` for HTTP check). | - |
32 | | -t | Timeout in seconds. | 30 |
33 | | -h | The HTTP endpoint to check. | 127.0.0.1 |
34 | | -http-status | Expected HTTP status code. | 200 |
35 | | -http-text | Substring to search for in the HTTP response. | - |
36 | | -http-follow-redirect | Whether to follow redirects (`true` or `false`). | true |
37 |
38 | ## Notes
39 | {{< callout type="info" >}}
40 | - Environment variables override command-line arguments.
41 | - If `W4IT_HTTP_STATUS_CODE` is not defined, the check defaults to expecting status code `200`.
42 | - If `W4IT_HTTP_TEXT` is not defined, only the status code is checked.
43 | {{< /callout >}}
44 |
45 | ## Exit Codes
46 | | Code | Meaning |
47 | |------|------------------------------------|
48 | | 0 | Connection successful. |
49 | | 1 | Timed out. |
50 | | 2 | Validation error or incorrect input.
51 |
52 |
--------------------------------------------------------------------------------
/docs/content/docs/Installation/_index.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2024-12-24T21:25:42+01:00'
3 | draft = false
4 | title = 'Installation'
5 | weight = 10
6 | +++
7 | # Installation Guide for Wait4it
8 |
9 | To get started with Wait4it, you can either download the latest release or build it yourself. Follow the instructions below based on your preference.
10 |
11 | ## Option 1: Download the Latest Release
12 |
13 | The easiest way to get started is by downloading the latest release of Wait4it.
14 |
15 | 1. Visit the [Wait4it Releases Page](https://github.com/ph4r5h4d/wait4it/releases).
16 | 2. Download the appropriate binary for your operating system (Linux, macOS, or Windows).
17 | 3. Extract the downloaded file and move it to a directory of your choice.
18 | 4. Ensure that the directory is included in your system's `PATH` to run Wait4it from anywhere in your terminal.
19 |
20 | ## Option 2: Build Wait4it Yourself
21 |
22 | If you prefer to build Wait4it from source, follow these steps:
23 |
24 | ### Prerequisites
25 |
26 | Make sure you have [Go](https://golang.org/dl/) installed on your machine.
27 |
28 | ### Build Instructions
29 |
30 | 1. Clone the Wait4it repository using `git` or the GitHub CLI:
31 |
32 | - Using `git`:
33 | ```bash
34 | git clone https://github.com/ph4r5h4d/wait4it.git
35 | ```
36 |
37 | - Using GitHub CLI (`gh`):
38 | ```bash
39 | gh repo clone ph4r5h4d/wait4it
40 | ```
41 |
42 | 2. Navigate to the project directory:
43 | ```bash
44 | cd wait4it
45 | ```
46 |
47 | 3. Build the project:
48 | ```bash
49 | go build
50 | ```
51 |
52 | 4. After the build process completes, the `wait4it` binary will be created in the current directory.
53 |
54 | 5. Move the binary to a directory in your `PATH` for easy access:
55 | ```bash
56 | mv wait4it /usr/local/bin/
57 | ```
58 |
59 | 6. Verify the installation by running:
60 | ```bash
61 | wait4it --version
62 | ```
63 |
64 | Now you're ready to start using Wait4it!
65 |
66 | ---
67 |
68 | For more details on how to use Wait4it, refer to the left sidebar for specific checks and their usage instructions.
69 |
70 |
--------------------------------------------------------------------------------
/docs/content/docs/Kafka/_index.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2025-03-23T11:25:42+01:00'
3 | draft = false
4 | title = 'Kafka'
5 | +++
6 | Kafka Check validates connectivity to a Kafka instance. This can check the connection to the leader instance,
7 | or try to connect to the leader via a non-leader instance.
8 | **This module support is beta. No authentication scheme is currently supported**
9 |
10 | ## Usage with Binary
11 | ```bash
12 | ./wait4it -type=kafka -h=127.0.0.1 -p=9092 -kafka-connect-to-leader-via-non-leader=false -t=60
13 | ```
14 |
15 | ## Usage with Docker
16 | ```bash
17 | docker run ph4r5h4d/wait4it -type=kafka -h=127.0.0.1 -p=9092 -kafka-connect-to-leader-via-non-leader=false -t=60
18 | ```
19 |
20 | ## Environment Variables
21 | | Variable | Description | Default |
22 | |--------------------------------------|--------------------------------------------------------|-----------|
23 | | W4IT_TYPE | The type of check (set to `kafka` for Kafka check). | - |
24 | | W4IT_TIMEOUT | Timeout in seconds. | 30 |
25 | | W4IT_HOST | The host to check. | 127.0.0.1 |
26 | | W4IT_PORT | The port to check on the Kafka host. | 9092 |
27 | | W4IT_KAFKA_CONNECTION_TYPE | The connection type toward Kafka | tcp |
28 | | W4IT_KAFKA_CONNECT_TO_LEADER_VIA_NON_LEADER | Whether to try to connect to a leader via a non-leader | false |
29 |
30 | ## Command-Line Arguments
31 | | Argument | Description | Default |
32 | |--------------------------------------|---------------------------------------------------------------------------|-----------|
33 | | -type | The type of check (set to `kafka` for Kafka check). | - |
34 | | -t | Timeout in seconds. | 30 |
35 | | -h | The host to check. | 127.0.0.1 |
36 | | -p | The port to check on the Kafka host. | 9092 |
37 | | -kafka-connection-type | The connection type toward Kafka | tcp |
38 | | -kafka-connect-to-leader-via-non-leader | Whether to try to connect to a leader via a non-leader | false |
39 | ## Notes
40 |
41 | {{< callout type="info" >}}
42 | - Environment variables override command-line arguments.
43 | - Authentication scheme is not supported yet.
44 | - If you have a requirement for authentication, please open an issue.
45 | {{< /callout >}}
46 |
47 | ## Exit Codes
48 | | Code | Meaning |
49 | |------|------------------------------------|
50 | | 0 | Connection successful. |
51 | | 1 | Timed out. |
52 | | 2 | Validation error or incorrect input.|
53 |
--------------------------------------------------------------------------------
/docs/content/docs/Memcached/_index.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2024-12-24T21:25:42+01:00'
3 | draft = false
4 | title = 'Memcached'
5 | +++
6 |
7 | Memcached Check validates connectivity to a Memcached instance.
8 |
9 | ## Usage with Binary
10 | ./wait4it -type=memcached -h=127.0.0.1 -p=11211 -t=60
11 |
12 | ## Usage with Docker
13 | docker run ph4r5h4d/wait4it -type=memcached -h=127.0.0.1 -p=11211 -t=60
14 |
15 | ## Environment Variables
16 |
17 | | Variable | Description | Default |
18 | |------------------|---------------------------------------------------------------------------|-----------|
19 | | W4IT_TYPE | The type of check (set to `memcached` for Memcached check). | - |
20 | | W4IT_TIMEOUT | Timeout in seconds. | 30 |
21 | | W4IT_HOST | The host to check. | 127.0.0.1 |
22 | | W4IT_PORT | The port to check on the Memcached host. | 11211 |
23 |
24 | ## Command-Line Arguments
25 |
26 | | Argument | Description | Default |
27 | |------------------|---------------------------------------------------------------------------|-----------|
28 | | -type | The type of check (set to `memcached`). | - |
29 | | -t | Timeout in seconds. | 30 |
30 | | -h | The host to check. | 127.0.0.1 |
31 | | -p | The port to check on the Memcached host. | 11211 |
32 |
33 | ## Notes
34 | {{< callout type="info" >}}
35 | - Environment variables override command-line arguments.
36 | - Currently, multiple hosts and cluster checks are not supported.
37 | {{< /callout >}}
38 |
39 | ## Exit Codes
40 | | Code | Meaning |
41 | |------|------------------------------------|
42 | | 0 | Connection successful. |
43 | | 1 | Timed out. |
44 | | 2 | Validation error or incorrect input.
45 |
--------------------------------------------------------------------------------
/docs/content/docs/MongoDB/_index.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2024-12-24T21:25:42+01:00'
3 | draft = false
4 | title = 'MongoDB'
5 | +++
6 | MongoDB Check validates connectivity to a MongoDB instance.
7 |
8 | ## Usage with Binary
9 | ```bash
10 | ./wait4it -type=mongo -h=127.0.0.1 -p=27017 -t=60 -u=mongoadmin -P=secret
11 | ```
12 |
13 | ## Usage with Docker
14 | ```bash
15 | docker run ph4r5h4d/wait4it -type=mongo -h=127.0.0.1 -p=27017 -t=60 -u=mongoadmin -P=secret
16 | ```
17 |
18 | ## Environment Variables
19 |
20 | | Variable | Description | Default |
21 | |--------------|-------------------------------------------------------------------------|-----------|
22 | | W4IT_TYPE | The type of check (set to `mongo` for MongoDB check). | - |
23 | | W4IT_TIMEOUT | Timeout in seconds. | 30 |
24 | | W4IT_HOST | The host to check. | 127.0.0.1 |
25 | | W4IT_PORT | The port to check on the MongoDB host. | 27017 |
26 | | W4IT_USERNAME| The username for MongoDB authentication. | - |
27 | | W4IT_PASSWORD| The password for MongoDB authentication. | - |
28 |
29 | ## Command-Line Arguments
30 |
31 | | Argument | Description | Default |
32 | |---------------|-------------------------------------------------------------------------|-----------|
33 | | -type | The type of check (set to `mongo`). | - |
34 | | -t | Timeout in seconds. | 30 |
35 | | -h | The host to check. | 127.0.0.1 |
36 | | -p | The port to check on the MongoDB host. | 27017 |
37 | | -u | The username for MongoDB authentication. | - |
38 | | -P | The password for MongoDB authentication. | - |
39 |
40 | ## Notes
41 | {{< callout type="info" >}}
42 | - Environment variables override command-line arguments.
43 | - Currently, only username/password authentication mechanism is supported.
44 | {{< /callout >}}
45 |
46 | ## Exit Codes
47 | | Code | Meaning |
48 | |------|------------------------------------|
49 | | 0 | Connection successful. |
50 | | 1 | Timed out. |
51 | | 2 | Validation error or incorrect input.
52 |
--------------------------------------------------------------------------------
/docs/content/docs/MySQL/_index.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2024-12-24T21:25:42+01:00'
3 | draft = false
4 | title = 'MySQL Check'
5 | +++
6 | MySQL Check validates connectivity to a MySQL database and optionally checks for specific database availability.
7 |
8 | ## Usage with Binary
9 | ```bash
10 | ./wait4it -type=mysql -h=127.0.0.1 -p=3306 -t=60 -u=root -P=secret -n=app
11 | ```
12 |
13 | ## Usage with Docker
14 | ```bash
15 | docker run ph4r5h4d/wait4it -type=mysql -h=127.0.0.1 -p=3306 -t=60 -u=root -P=secret -n=app
16 | ```
17 |
18 | ## Environment Variables
19 |
20 | | Variable | Description | Default |
21 | |--------------|---------------------------------------------------------------------------|-----------|
22 | | W4IT_TYPE | The type of check (set to `mysql` for MySQL check). | - |
23 | | W4IT_TIMEOUT | Timeout in seconds. | 30 |
24 | | W4IT_HOST | The host to check. | 127.0.0.1 |
25 | | W4IT_PORT | The port to check on the MySQL host. | 3306 |
26 | | W4IT_USERNAME| The username for MySQL authentication. | - |
27 | | W4IT_PASSWORD| The password for MySQL authentication. | - |
28 | | W4IT_DBNAME | The name of the database to check. | - |
29 |
30 | ## Command-Line Arguments
31 |
32 | | Argument | Description | Default |
33 | |---------------|---------------------------------------------------------------------------|-----------|
34 | | -type | The type of check (set to `mysql`). | - |
35 | | -t | Timeout in seconds. | 30 |
36 | | -h | The host to check. | 127.0.0.1 |
37 | | -p | The port to check on the MySQL host. | 3306 |
38 | | -u | The username for MySQL authentication. | - |
39 | | -P | The password for MySQL authentication. | - |
40 | | -n | The name of the database to check. | - |
41 |
42 | ## Notes
43 | {{< callout type="info" >}}
44 | - Environment variables override command-line arguments.
45 | - Ensure that the specified MySQL database and username/password are correct for the connection to succeed.
46 | {{< /callout >}}
47 |
48 | ## Exit Codes
49 | | Code | Meaning |
50 | |------|------------------------------------|
51 | | 0 | Connection successful. |
52 | | 1 | Timed out. |
53 | | 2 | Validation error or incorrect input.
54 |
--------------------------------------------------------------------------------
/docs/content/docs/PostgreSQL/_index.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2024-12-24T21:25:42+01:00'
3 | draft = false
4 | title = 'PostgreSQL Check'
5 | +++
6 | PostgreSQL Check validates connectivity to a PostgreSQL database and optionally checks for SSL mode and database availability.
7 |
8 | ## Usage with Binary
9 | ```bash
10 | ./wait4it -type=postgres -h=127.0.0.1 -p=5432 -t=60 -u=postgres -P=secret -ssl=disable
11 | ```
12 |
13 | ## Usage with Docker
14 | ```bash
15 | docker run ph4r5h4d/wait4it -type=postgres -h=127.0.0.1 -p=5432 -t=60 -u=postgres -P=secret -ssl=disable
16 | ```
17 |
18 | ## Environment Variables
19 |
20 | | Variable | Description | Default |
21 | |-----------------|---------------------------------------------------------------------------|-----------|
22 | | W4IT_TYPE | The type of check (set to `postgres` for PostgreSQL check). | - |
23 | | W4IT_TIMEOUT | Timeout in seconds. | 30 |
24 | | W4IT_HOST | The host to check. | 127.0.0.1 |
25 | | W4IT_PORT | The port to check on the PostgreSQL host. | 5432 |
26 | | W4IT_USERNAME | The username for PostgreSQL authentication. | - |
27 | | W4IT_PASSWORD | The password for PostgreSQL authentication. | - |
28 | | W4IT_DBNAME | The name of the database to check. | - |
29 | | W4IT_SSL_MODE | Whether to enable or disable SSL mode for PostgreSQL (`enable` or `disable`). | `disable` |
30 |
31 | ## Command-Line Arguments
32 |
33 | | Argument | Description | Default |
34 | |-----------------|---------------------------------------------------------------------------|-----------|
35 | | -type | The type of check (set to `postgres`). | - |
36 | | -t | Timeout in seconds. | 30 |
37 | | -h | The host to check. | 127.0.0.1 |
38 | | -p | The port to check on the PostgreSQL host. | 5432 |
39 | | -u | The username for PostgreSQL authentication. | - |
40 | | -P | The password for PostgreSQL authentication. | - |
41 | | -n | The name of the database to check. | - |
42 | | -ssl | Whether to enable or disable SSL mode for PostgreSQL (`disable` or `enable`). | `disable` |
43 |
44 | ## Notes
45 | {{< callout type="info" >}}
46 | - Environment variables override command-line arguments.
47 | - If `W4IT_SSL_MODE` is not defined, SSL is disabled by default.
48 | - Ensure that the specified PostgreSQL database, username, and password are correct for the connection to succeed.
49 | - {{< /callout >}}
50 |
51 | ## Exit Codes
52 | | Code | Meaning |
53 | |------|------------------------------------|
54 | | 0 | Connection successful. |
55 | | 1 | Timed out. |
56 | | 2 | Validation error or incorrect input.
57 |
--------------------------------------------------------------------------------
/docs/content/docs/RabbitMQ/_index.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2024-12-24T21:25:42+01:00'
3 | draft = false
4 | title = 'RabbitMQ'
5 | +++
6 | RabbitMQ Check validates connectivity to a RabbitMQ instance and optionally checks for username and password authentication.
7 |
8 | ## Usage with Binary
9 | ./wait4it -type=rabbitmq -h=127.0.0.1 -p=5267 -t=60 -u=guest -P=guest
10 |
11 | ## Usage with Docker
12 | docker run ph4r5h4d/wait4it -type=rabbitmq -h=127.0.0.1 -p=5267 -t=60 -u=guest -P=guest
13 |
14 | ## Environment Variables
15 |
16 | | Variable | Description | Default |
17 | |------------------|---------------------------------------------------------------------------|-----------|
18 | | W4IT_TYPE | The type of check (set to `rabbitmq` for RabbitMQ check). | - |
19 | | W4IT_TIMEOUT | Timeout in seconds. | 30 |
20 | | W4IT_HOST | The host to check. | 127.0.0.1 |
21 | | W4IT_PORT | The port to check on the RabbitMQ host. | 5267 |
22 | | W4IT_USERNAME | The username for RabbitMQ authentication. | - |
23 | | W4IT_PASSWORD | The password for RabbitMQ authentication. | - |
24 |
25 | ## Command-Line Arguments
26 |
27 | | Argument | Description | Default |
28 | |------------------|---------------------------------------------------------------------------|-----------|
29 | | -type | The type of check (set to `rabbitmq`). | - |
30 | | -t | Timeout in seconds. | 30 |
31 | | -h | The host to check. | 127.0.0.1 |
32 | | -p | The port to check on the RabbitMQ host. | 5267 |
33 | | -u | The username for RabbitMQ authentication. | - |
34 | | -P | The password for RabbitMQ authentication. | - |
35 |
36 | ## Notes
37 | {{< callout type="info" >}}
38 | - Environment variables override command-line arguments.
39 | - Ensure that the specified RabbitMQ username and password are correct for the connection to succeed.
40 | - {{< /callout >}}
41 |
42 | ## Exit Codes
43 | | Code | Meaning |
44 | |------|------------------------------------|
45 | | 0 | Connection successful. |
46 | | 1 | Timed out. |
47 | | 2 | Validation error or incorrect input.
48 |
--------------------------------------------------------------------------------
/docs/content/docs/Redis/_index.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2024-12-24T21:25:42+01:00'
3 | draft = false
4 | title = 'Redis'
5 | +++
6 | Redis Check validates connectivity to a Redis instance and optionally checks for authentication and operation mode (standalone or cluster).
7 |
8 | ## Usage with Binary
9 | ```bash
10 | ./wait4it -type=redis -h=127.0.0.1 -p=6379 -t=60 -P=secret
11 | ```
12 |
13 | ## Usage with Docker
14 | ```bash
15 | docker run ph4r5h4d/wait4it -type=redis -h=127.0.0.1 -p=6379 -t=60 -P=secret
16 | ```
17 |
18 | ## Environment Variables
19 |
20 | | Variable | Description | Default |
21 | |------------------|---------------------------------------------------------------------------|-----------|
22 | | W4IT_TYPE | The type of check (set to `redis` for Redis check). | - |
23 | | W4IT_TIMEOUT | Timeout in seconds. | 30 |
24 | | W4IT_HOST | The host to check. | 127.0.0.1 |
25 | | W4IT_PORT | The port to check on the Redis host. | 6379 |
26 | | W4IT_PASSWORD | The password for Redis authentication (if any). | - |
27 | | W4IT_OPERATION_MODE | The Redis operation mode (`standalone` or `cluster`). | `standalone` |
28 |
29 | ## Command-Line Arguments
30 |
31 | | Argument | Description | Default |
32 | |--------------------|---------------------------------------------------------------------------|-----------|
33 | | -type | The type of check (set to `redis`). | - |
34 | | -t | Timeout in seconds. | 30 |
35 | | -h | The host to check. | 127.0.0.1 |
36 | | -p | The port to check on the Redis host. | 6379 |
37 | | -P | The password for Redis authentication (if any). | - |
38 | | -operation-mode | The Redis operation mode (`standalone` or `cluster`). | `standalone` |
39 |
40 | ## Notes
41 | {{< callout type="info" >}}
42 | - Environment variables override command-line arguments.
43 | - If `W4IT_OPERATION_MODE` is not defined, it defaults to `standalone`.
44 | - Redis Sentinel is not supported yet.
45 | - This version can only check one host within a Redis cluster; support for checking multiple hosts in a Redis cluster might be added upon request.
46 | {{< /callout >}}
47 |
48 |
49 | ## Exit Codes
50 | | Code | Meaning |
51 | |------|------------------------------------|
52 | | 0 | Connection successful. |
53 | | 1 | Timed out. |
54 | | 2 | Validation error or incorrect input.
55 |
--------------------------------------------------------------------------------
/docs/content/docs/TCP/_index.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2024-12-24T21:25:42+01:00'
3 | draft = false
4 | title = 'TCP Port Check'
5 | +++
6 | TCP Port Check validates connectivity to a specific TCP port on a target host within a given timeout.
7 |
8 | ## Usage with Binary
9 | ```bash
10 | ./wait4it -type=tcp -h=127.0.0.1 -p=8080 -t=60
11 | ```
12 |
13 | ## Usage with Docker
14 | ```bash
15 | docker run ph4r5h4d/wait4it -type=tcp -h=127.0.0.1 -p=8080 -t=60
16 | ```
17 |
18 | ## Environment Variables
19 |
20 | | Variable | Description | Default |
21 | |----------------|---------------------------------------|-----------|
22 | | W4IT_TYPE | The type of check (set to `tcp`). | - |
23 | | W4IT_TIMEOUT | Timeout in seconds. | 30 |
24 | | W4IT_HOST | The host to check. | 127.0.0.1 |
25 | | W4IT_PORT | The TCP port to check. | - |
26 |
27 | ## Command-Line Arguments
28 |
29 | | Argument | Description | Default |
30 | |----------|---------------------------------------|-----------|
31 | | -type | The type of check (set to `tcp`). | - |
32 | | -t | Timeout in seconds. | 30 |
33 | | -h | The host to check. | 127.0.0.1 |
34 | | -p | The TCP port to check. | - |
35 |
36 | ## Notes
37 | - Environment variables override command-line arguments.
38 | - Ensure the target host and port are reachable within the timeout period.
39 |
40 | ## Exit Codes
41 | | Code | Meaning |
42 | |------|------------------------------------|
43 | | 0 | Connection successful. |
44 | | 1 | Timed out. |
45 | | 2 | Validation error or incorrect input.
46 |
--------------------------------------------------------------------------------
/docs/content/docs/_index.md:
--------------------------------------------------------------------------------
1 | +++
2 | date = '2024-12-24T21:25:42+01:00'
3 | draft = false
4 | title = 'Wait4it'
5 | +++
6 | To see the details of each supported service, use the left navigation bar.
7 |
--------------------------------------------------------------------------------
/docs/content/index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ph4r5h4d/wait4it/411b0a7c3ac96698ca4e38d644e51ba2fd45b6f5/docs/content/index.png
--------------------------------------------------------------------------------
/docs/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ph4r5h4d/wait4it
2 |
3 | go 1.23.4
4 |
5 | require (
6 | github.com/imfing/hextra v0.9.1 // indirect
7 | github.com/klauspost/compress v1.15.9 // indirect
8 | github.com/pierrec/lz4/v4 v4.1.15 // indirect
9 | github.com/segmentio/kafka-go v0.4.47 // indirect
10 | )
11 |
--------------------------------------------------------------------------------
/docs/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3 | github.com/imfing/hextra v0.9.1 h1:7zkX7bFcE3+sluuvcKImWm2xWU4W40vTJXVjzpgeK+Y=
4 | github.com/imfing/hextra v0.9.1/go.mod h1:cEfel3lU/bSx7lTE/+uuR4GJaphyOyiwNR3PTqFTXpI=
5 | github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
6 | github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
7 | github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
8 | github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
9 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
10 | github.com/segmentio/kafka-go v0.4.47 h1:IqziR4pA3vrZq7YdRxaT3w1/5fvIH5qpCwstUanQQB0=
11 | github.com/segmentio/kafka-go v0.4.47/go.mod h1:HjF6XbOKh0Pjlkr5GVZxt6CsjjwnmhVOfURM5KMd8qg=
12 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
13 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
14 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
15 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
16 | github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
17 | github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
18 | github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
19 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
20 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
21 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
22 | golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
23 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
24 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
25 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
26 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
27 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
28 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
29 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
30 | golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
31 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
32 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
33 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
34 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
35 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
36 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
37 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
38 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
39 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
40 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
41 | golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
42 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
43 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
44 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
45 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
46 | golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
47 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
48 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
49 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
50 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
51 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
52 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
53 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
54 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
55 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
56 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
57 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
58 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
59 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
60 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
61 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
62 |
--------------------------------------------------------------------------------
/docs/hugo.yaml:
--------------------------------------------------------------------------------
1 | baseURL: https://wait4it.dev/
2 | languageCode: en-us
3 | title: Wait4it
4 | module:
5 | imports:
6 | - path: github.com/imfing/hextra
7 | menu:
8 | main:
9 | - name: Documentation
10 | pageRef: /docs
11 | weight: 1
12 | - name: About
13 | pageRef: /about
14 | weight: 3
15 | - name: Search
16 | weight: 4
17 | params:
18 | type: search
19 | - name: GitHub
20 | weight: 5
21 | url: "https://github.com/ph4r5h4d/wait4it"
22 | params:
23 | icon: github
24 | params:
25 | navbar:
26 | displayTitle: true
27 | displayLogo: true
28 | logo:
29 | path: /wait4it.svg
30 | dark: /wait4it-dark.svg
31 | link: /
32 | width: 40
33 | height: 20
34 |
--------------------------------------------------------------------------------
/docs/i18n/en.yaml:
--------------------------------------------------------------------------------
1 | backToTop: "Scroll to top"
2 | changeLanguage: "Change language"
3 | changeTheme: "Change theme"
4 | copyCode: "Copy code"
5 | copyright: "© 2021-2025 Wait4it."
6 | dark: "Dark"
7 | editThisPage: "Edit this page on GitHub →"
8 | lastUpdated: "Last updated on"
9 | light: "Light"
10 | noResultsFound: "No results found."
11 | onThisPage: "On this page"
12 | poweredBy: "Powered by Hextra"
13 | readMore: "Read more →"
14 | searchPlaceholder: "Search..."
15 |
--------------------------------------------------------------------------------
/docs/static/wait4it-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ph4r5h4d/wait4it/411b0a7c3ac96698ca4e38d644e51ba2fd45b6f5/docs/static/wait4it-dark.png
--------------------------------------------------------------------------------
/docs/static/wait4it.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ph4r5h4d/wait4it/411b0a7c3ac96698ca4e38d644e51ba2fd45b6f5/docs/static/wait4it.png
--------------------------------------------------------------------------------
/docs/static/wait4it.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module wait4it
2 |
3 | go 1.23.0
4 |
5 | toolchain go1.24.2
6 |
7 | require (
8 | github.com/aerospike/aerospike-client-go v4.5.2+incompatible
9 | github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf
10 | github.com/elastic/go-elasticsearch/v8 v8.18.0
11 | github.com/go-redis/redis/v8 v8.11.5
12 | github.com/go-sql-driver/mysql v1.9.2
13 | github.com/lib/pq v1.10.9
14 | github.com/pkg/errors v0.9.1
15 | github.com/rabbitmq/amqp091-go v1.10.0
16 | github.com/segmentio/kafka-go v0.4.48
17 | go.mongodb.org/mongo-driver v1.17.3
18 | )
19 |
20 | require (
21 | filippo.io/edwards25519 v1.1.0 // indirect
22 | github.com/cespare/xxhash/v2 v2.3.0 // indirect
23 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
24 | github.com/elastic/elastic-transport-go/v8 v8.7.0 // indirect
25 | github.com/go-logr/logr v1.4.2 // indirect
26 | github.com/go-logr/stdr v1.2.2 // indirect
27 | github.com/golang/snappy v1.0.0 // indirect
28 | github.com/klauspost/compress v1.18.0 // indirect
29 | github.com/montanaflynn/stats v0.7.1 // indirect
30 | github.com/pierrec/lz4/v4 v4.1.22 // indirect
31 | github.com/xdg-go/pbkdf2 v1.0.0 // indirect
32 | github.com/xdg-go/scram v1.1.2 // indirect
33 | github.com/xdg-go/stringprep v1.0.4 // indirect
34 | github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
35 | github.com/yuin/gopher-lua v1.1.1 // indirect
36 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect
37 | go.opentelemetry.io/otel v1.35.0 // indirect
38 | go.opentelemetry.io/otel/metric v1.35.0 // indirect
39 | go.opentelemetry.io/otel/trace v1.35.0 // indirect
40 | golang.org/x/crypto v0.37.0 // indirect
41 | golang.org/x/sync v0.13.0 // indirect
42 | golang.org/x/text v0.24.0 // indirect
43 | )
44 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
2 | filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
3 | github.com/aerospike/aerospike-client-go v4.5.2+incompatible h1:G7cGT9bbOEJwPR8sKrXNP/PotN25Y5pfd8QrLbg3eTY=
4 | github.com/aerospike/aerospike-client-go v4.5.2+incompatible/go.mod h1:zj8LBEnWBDOVEIJt8LvaRvDG5ARAoa5dBeHaB472NRc=
5 | github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf h1:TqhNAT4zKbTdLa62d2HDBFdvgSbIGB3eJE8HqhgiL9I=
6 | github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
7 | github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
8 | github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
9 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
11 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
12 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
13 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
14 | github.com/elastic/elastic-transport-go/v8 v8.7.0 h1:OgTneVuXP2uip4BA658Xi6Hfw+PeIOod2rY3GVMGoVE=
15 | github.com/elastic/elastic-transport-go/v8 v8.7.0/go.mod h1:YLHer5cj0csTzNFXoNQ8qhtGY1GTvSqPnKWKaqQE3Hk=
16 | github.com/elastic/go-elasticsearch/v8 v8.18.0 h1:ANNq1h7DEiPUaALb8+5w3baQzaS08WfHV0DNzp0VG4M=
17 | github.com/elastic/go-elasticsearch/v8 v8.18.0/go.mod h1:WLqwXsJmQoYkoA9JBFeEwPkQhCfAZuUvfpdU/NvSSf0=
18 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
19 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
20 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
21 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
22 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
23 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
24 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
25 | github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
26 | github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
27 | github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
28 | github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
29 | github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
30 | github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
31 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
32 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
33 | github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
34 | github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
35 | github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
36 | github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
37 | github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
38 | github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
39 | github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
40 | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
41 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
42 | github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
43 | github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
44 | github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
45 | github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
46 | github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
47 | github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
48 | github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
49 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
50 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
51 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
52 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
53 | github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
54 | github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
55 | github.com/segmentio/kafka-go v0.4.48 h1:9jyu9CWK4W5W+SroCe8EffbrRZVqAOkuaLd/ApID4Vs=
56 | github.com/segmentio/kafka-go v0.4.48/go.mod h1:HjF6XbOKh0Pjlkr5GVZxt6CsjjwnmhVOfURM5KMd8qg=
57 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
58 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
59 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
60 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
61 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
62 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
63 | github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
64 | github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
65 | github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
66 | github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
67 | github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
68 | github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
69 | github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
70 | github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
71 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
72 | github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
73 | github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
74 | go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ=
75 | go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
76 | go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
77 | go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
78 | go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
79 | go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
80 | go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
81 | go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
82 | go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
83 | go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
84 | go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
85 | go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
86 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
87 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
88 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
89 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
90 | golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
91 | golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
92 | golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
93 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
94 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
95 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
96 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
97 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
98 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
99 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
100 | golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
101 | golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
102 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
103 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
104 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
105 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
106 | golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
107 | golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
108 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
109 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
110 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
111 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
112 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
113 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
114 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
115 | golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
116 | golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
117 | golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
118 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
119 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
120 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
121 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
122 | golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
123 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
124 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
125 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
126 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
127 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
128 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
129 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
130 | golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
131 | golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
132 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
133 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
134 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
135 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
136 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
137 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
138 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
139 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
140 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
141 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
142 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
143 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
144 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
145 |
--------------------------------------------------------------------------------
/internal/banner/banner.go:
--------------------------------------------------------------------------------
1 | package banner
2 |
3 | import "fmt"
4 |
5 | func Banner() {
6 | fmt.Println(applicationName() + sponsor() + support())
7 | }
8 |
9 | func applicationName() string {
10 | return " _ __ _ __ __ __ _ __ \n" +
11 | "| | / / ____ _ (_) / /_ / // / (_) / /_\n" +
12 | "| | /| / / / __ `/ / / / __/ / // /_ / / / __/\n" +
13 | "| |/ |/ / / /_/ / / / / /_ /__ __/ / / / /_ \n" +
14 | "|__/|__/ \\__,_/ /_/ \\__/ /_/ /_/ \\__/ \n\n"
15 | }
16 |
17 | func sponsor() string {
18 | return "You can buy me a coffee or sponsor wait4it via: \nhttps://paypal.me/ph4r5h4d \n" +
19 | "or\nhttps://github.com/sponsors/ph4r5h4d\n\n"
20 | }
21 |
22 | func support() string {
23 | return "For NFR and issues go to: https://github.com/ph4r5h4d/wait4it/issues\n\n"
24 | }
25 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "flag"
6 | "fmt"
7 | "log"
8 | "os"
9 | "strconv"
10 | "wait4it/internal/banner"
11 |
12 | "wait4it/pkg/check"
13 | "wait4it/pkg/model"
14 | )
15 |
16 | func defaultEnv(env string, def string) string {
17 | v := os.Getenv(env)
18 | if v == "" {
19 | return def
20 | }
21 |
22 | return v
23 | }
24 |
25 | func defaultEnvInt(env string, def int) int {
26 | v := os.Getenv(env)
27 | if v == "" {
28 | return def
29 | }
30 | i, err := strconv.ParseInt(v, 10, 0)
31 | if err != nil {
32 | return def
33 | }
34 | return int(i)
35 | }
36 |
37 | func defaultEnvBool(env string, def bool) bool {
38 | v := os.Getenv(env)
39 | if v == "" {
40 | return def
41 | }
42 | if v == "true" || v == "1" {
43 | return true
44 | }
45 | return false
46 | }
47 |
48 | func main() {
49 | cfg := &model.CheckContext{}
50 | flag.StringVar(&cfg.Config.CheckType, "type", defaultEnv("W4IT_TYPE", "tcp"), "define the type of check")
51 | flag.IntVar(&cfg.Config.Timeout, "t", defaultEnvInt("W4IT_TIMEOUT", 30), "Timeout, amount of time wait4it waits for the port in seconds")
52 | flag.StringVar(&cfg.Host, "h", defaultEnv("W4IT_HOST", "127.0.0.1"), "IP of the host you want to test against")
53 | flag.IntVar(&cfg.Port, "p", defaultEnvInt("W4IT_PORT", 0), "Port")
54 | flag.StringVar(&cfg.Username, "u", defaultEnv("W4IT_USERNAME", ""), "Username of the service")
55 | flag.StringVar(&cfg.Password, "P", defaultEnv("W4IT_PASSWORD", ""), "Password of the service, it picks the W4IT_PASSWORD env if it is empty")
56 | flag.StringVar(&cfg.DatabaseName, "n", defaultEnv("W4IT_DBNAME", ""), "Name of the database")
57 | flag.StringVar(&cfg.DBConf.SSLMode, "ssl", defaultEnv("W4IT_SSL_MODE", "disable"), "Enable or Disable ssl mode (for some database or services)")
58 | flag.StringVar(&cfg.DBConf.OperationMode, "operation-mode", defaultEnv("W4IT_OPERATION_MODE", "standalone"), "choose operation mode (for some database or services)")
59 | flag.IntVar(&cfg.HttpConf.StatusCode, "status-code", defaultEnvInt("W4IT_HTTP_STATUS_CODE", 200), "Status code to be expected from http call")
60 | flag.StringVar(&cfg.HttpConf.Text, "http-text", defaultEnv("W4IT_HTTP_TEXT", ""), "Text to check inside http response")
61 | flag.BoolVar(&cfg.HttpConf.FollowRedirect, "http-follow-redirect", defaultEnvBool("W4IT_HTTP_FOLLOW_REDIRECT", true), "Whether to follow the redirect while doing the HTTP check")
62 | flag.StringVar(&cfg.KafkaConf.ConnectionType, "kafka-connection-type", defaultEnv("W4IT_KAFKA_CONNECTION_TYPE", "tcp"), "Kafka Connection Type, default is tcp")
63 | flag.BoolVar(&cfg.KafkaConf.ConnectToLeaderViaNonLeader, "kafka-connect-to-leader-via-non-leader", defaultEnvBool("W4IT_KAFKA_CONNECT_TO_LEADER_VIA_NON_LEADER", false), "Whether to connect to leader via non-leader, default is false")
64 |
65 | flag.Parse()
66 | // We don't want to show password in help message
67 | if cfg.Password == "" {
68 | defaultEnv("W4IT_PASSWORD", "")
69 | }
70 | cfg.Progress = func(s string) {
71 | fmt.Print(s)
72 | }
73 | banner.Banner()
74 | if err := check.RunCheck(context.Background(), cfg); err != nil {
75 | log.Fatal(err)
76 | }
77 | log.Println("Success!")
78 | }
79 |
--------------------------------------------------------------------------------
/pkg/aerospike/aerospike.go:
--------------------------------------------------------------------------------
1 | package aerospike
2 |
3 | import (
4 | "context"
5 | "errors"
6 |
7 | "wait4it/pkg/model"
8 |
9 | "github.com/aerospike/aerospike-client-go"
10 | )
11 |
12 | func (m *AerospikeConnection) BuildContext(cx model.CheckContext) {
13 | m.Host = cx.Host
14 | m.Port = cx.Port
15 | }
16 |
17 | func (m *AerospikeConnection) Validate() error {
18 | if len(m.Host) == 0 {
19 | return errors.New("host can't be empty")
20 | }
21 |
22 | if m.Port < 1 || m.Port > 65535 {
23 | return errors.New("invalid port range for Memcached")
24 | }
25 |
26 | return nil
27 | }
28 |
29 | func (m *AerospikeConnection) Check(_ context.Context) (bool, bool, error) {
30 | // TODO: is it possible to handle ping using context?
31 | c, err := aerospike.NewClient(m.Host, m.Port)
32 |
33 | if err != nil {
34 | return false, false, err
35 | }
36 |
37 | if !c.IsConnected() {
38 | return false, false, errors.New("client is not connected")
39 | }
40 |
41 | return true, true, nil
42 | }
43 |
--------------------------------------------------------------------------------
/pkg/aerospike/structs.go:
--------------------------------------------------------------------------------
1 | package aerospike
2 |
3 | import (
4 | "wait4it/pkg/model"
5 | )
6 |
7 | type AerospikeConnection struct {
8 | Host string
9 | Port int
10 | }
11 |
12 | // NewChecker creates a new checker
13 | func NewChecker(c *model.CheckContext) (model.CheckInterface, error) {
14 | check := &AerospikeConnection{}
15 | check.BuildContext(*c)
16 | if err := check.Validate(); err != nil {
17 | return nil, err
18 | }
19 |
20 | return check, nil
21 | }
22 |
--------------------------------------------------------------------------------
/pkg/check/check-cmd.go:
--------------------------------------------------------------------------------
1 | package check
2 |
3 | import (
4 | "context"
5 | "time"
6 |
7 | "github.com/pkg/errors"
8 |
9 | "wait4it/pkg/model"
10 | )
11 |
12 | func RunCheck(ctx context.Context, c *model.CheckContext) error {
13 | cx, err := findCheckModule(c)
14 | if err != nil {
15 | return errors.Wrap(err, "can not find the module")
16 | }
17 |
18 | newCtx, cnl := context.WithTimeout(ctx, time.Duration(c.Config.Timeout)*time.Second)
19 | defer cnl()
20 |
21 | progress := c.Progress
22 | if progress == nil {
23 | progress = func(s string) {}
24 | }
25 |
26 | progress("Wait4it...")
27 |
28 | if err := ticker(newCtx, cx, progress); err != nil {
29 | return errors.Wrap(err, "check failed")
30 | }
31 |
32 | return nil
33 | }
34 |
35 | func findCheckModule(c *model.CheckContext) (model.CheckInterface, error) {
36 | newFunc, ok := cm[c.Config.CheckType]
37 | if !ok {
38 | return nil, errors.New("unsupported check type")
39 | }
40 |
41 | return newFunc(c)
42 | }
43 |
44 | func ticker(ctx context.Context, cs model.CheckInterface, progress func(string)) error {
45 | t := time.NewTicker(1 * time.Second)
46 | defer t.Stop()
47 | for {
48 | select {
49 | case <-ctx.Done():
50 | return ctx.Err()
51 | case <-t.C:
52 | r, err := check(ctx, cs)
53 | if err != nil {
54 | return errors.Wrap(err, "check failed")
55 | }
56 |
57 | if r {
58 | progress("\n")
59 | return nil
60 | }
61 |
62 | progress(".")
63 | }
64 | }
65 | }
66 |
67 | func check(ctx context.Context, cs model.CheckInterface) (bool, error) {
68 | r, eor, err := cs.Check(ctx)
69 | if err != nil && eor {
70 | return false, errors.Wrap(err, "failed")
71 | }
72 |
73 | return r, nil
74 | }
75 |
--------------------------------------------------------------------------------
/pkg/check/check-module-list.go:
--------------------------------------------------------------------------------
1 | package check
2 |
3 | import (
4 | "wait4it/pkg/aerospike"
5 | "wait4it/pkg/elasticsearch"
6 | "wait4it/pkg/http"
7 | "wait4it/pkg/kafka"
8 | "wait4it/pkg/memcached"
9 | "wait4it/pkg/model"
10 | "wait4it/pkg/mongodb"
11 | "wait4it/pkg/mysql"
12 | "wait4it/pkg/postgresql"
13 | "wait4it/pkg/rabbitmq"
14 | "wait4it/pkg/redis"
15 | "wait4it/pkg/tcp"
16 | )
17 |
18 | var cm = map[string]func(c *model.CheckContext) (model.CheckInterface, error){
19 | "tcp": tcp.NewChecker,
20 | "mysql": mysql.NewChecker,
21 | "postgres": postgresql.NewChecker,
22 | "http": http.NewChecker,
23 | "mongo": mongodb.NewChecker,
24 | "redis": redis.NewChecker,
25 | "rabbitmq": rabbitmq.NewChecker,
26 | "memcached": memcached.NewChecker,
27 | "elasticsearch": elasticsearch.NewChecker,
28 | "aerospike": aerospike.NewChecker,
29 | "kafka": kafka.NewChecker,
30 | }
31 |
--------------------------------------------------------------------------------
/pkg/elasticsearch/elasticsearch.go:
--------------------------------------------------------------------------------
1 | package elasticsearch
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "strconv"
7 | "wait4it/pkg/model"
8 |
9 | elasticsearch "github.com/elastic/go-elasticsearch/v8"
10 | )
11 |
12 | type ElasticSearchChecker struct {
13 | Host string
14 | Port int
15 | Username string
16 | Password string
17 | }
18 |
19 | func (esc *ElasticSearchChecker) BuildContext(cx model.CheckContext) {
20 | esc.Host = cx.Host
21 | esc.Port = cx.Port
22 | esc.Username = cx.Username
23 | esc.Password = cx.Password
24 | }
25 |
26 | func (esc *ElasticSearchChecker) Validate() error {
27 | if len(esc.Host) == 0 {
28 | return errors.New("Host can't be empty")
29 | }
30 |
31 | if esc.Port < 1 || esc.Port > 65535 {
32 | return errors.New("Invalid port range for ElasticSearch")
33 | }
34 |
35 | return nil
36 | }
37 |
38 | func (esc *ElasticSearchChecker) Check(ctx context.Context) (bool, bool, error) {
39 | cfg := elasticsearch.Config{
40 | Addresses: []string{
41 | esc.BuildConnectionString(),
42 | },
43 | Username: esc.Username,
44 | Password: esc.Password,
45 | }
46 |
47 | es, err := elasticsearch.NewClient(cfg)
48 | if err != nil {
49 | return false, true, err
50 | }
51 |
52 | if _, err := es.Ping(es.Ping.WithContext(ctx)); err != nil {
53 | return false, false, err
54 | }
55 |
56 | return true, true, nil
57 | }
58 |
59 | func (esc *ElasticSearchChecker) BuildConnectionString() string {
60 | return esc.Host + ":" + strconv.Itoa(esc.Port)
61 | }
62 |
63 | // NewChecker creates a new checker
64 | func NewChecker(c *model.CheckContext) (model.CheckInterface, error) {
65 | check := &ElasticSearchChecker{}
66 | check.BuildContext(*c)
67 | if err := check.Validate(); err != nil {
68 | return nil, err
69 | }
70 |
71 | return check, nil
72 | }
73 |
--------------------------------------------------------------------------------
/pkg/http/helpers.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import "net/url"
4 |
5 | func (h *HttpCheck) validateUrl() bool {
6 | _, err := url.ParseRequestURI(h.Url)
7 | if err != nil {
8 | return false
9 | }
10 |
11 | u, err := url.Parse(h.Url)
12 | if err != nil || u.Scheme == "" || u.Host == "" {
13 | return false
14 | }
15 |
16 | return true
17 | }
18 |
19 | func (h *HttpCheck) validateStatusCode() bool {
20 | // check against common status code
21 | if h.Status < 100 || h.Status > 599 {
22 | return false
23 | }
24 | return true
25 | }
26 |
--------------------------------------------------------------------------------
/pkg/http/http.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "io/ioutil"
7 | "net/http"
8 | "strings"
9 | "wait4it/pkg/model"
10 | )
11 |
12 | func (h *HttpCheck) BuildContext(cx model.CheckContext) {
13 | h.Url = cx.Host
14 | h.Status = cx.HttpConf.StatusCode
15 | if len(cx.HttpConf.Text) > 0 {
16 | h.Text = cx.HttpConf.Text
17 | }
18 | h.FollowRedirect = cx.HttpConf.FollowRedirect
19 | }
20 |
21 | func (h *HttpCheck) Validate() error {
22 | if !h.validateUrl() {
23 | return errors.New("invalid URL provided")
24 | }
25 |
26 | if !h.validateStatusCode() {
27 | return errors.New("invalid status code provided")
28 | }
29 |
30 | return nil
31 | }
32 |
33 | func (h *HttpCheck) Check(ctx context.Context) (bool, bool, error) {
34 | req, err := http.NewRequestWithContext(ctx, http.MethodGet, h.Url, nil)
35 | if err != nil {
36 | return false, false, err
37 | }
38 |
39 | resp, err := h.getClient().Do(req)
40 |
41 | if err != nil {
42 | return false, true, err
43 | }
44 | defer resp.Body.Close()
45 |
46 | body, err := ioutil.ReadAll(resp.Body)
47 | if err != nil {
48 | return false, true, err
49 | }
50 |
51 | if resp.StatusCode != h.Status {
52 | return false, false, errors.New("invalid status code")
53 | }
54 |
55 | if len(h.Text) > 0 {
56 | if !strings.Contains(string(body), h.Text) {
57 | return false, false, errors.New("can't find substring in response")
58 | }
59 | }
60 |
61 | return true, false, nil
62 | }
63 |
64 | func (h *HttpCheck) getClient() *http.Client {
65 | if h.FollowRedirect {
66 | return http.DefaultClient
67 | }
68 | return &http.Client{
69 | CheckRedirect: func(req *http.Request, via []*http.Request) error {
70 | return http.ErrUseLastResponse
71 | },
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/pkg/http/structs.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "wait4it/pkg/model"
5 | )
6 |
7 | type HttpCheck struct {
8 | Url string
9 | Status int
10 | Text string
11 | FollowRedirect bool
12 | }
13 |
14 | // NewChecker creates a new checker
15 | func NewChecker(c *model.CheckContext) (model.CheckInterface, error) {
16 | check := &HttpCheck{}
17 | check.BuildContext(*c)
18 | if err := check.Validate(); err != nil {
19 | return nil, err
20 | }
21 |
22 | return check, nil
23 | }
24 |
--------------------------------------------------------------------------------
/pkg/kafka/kafka.go:
--------------------------------------------------------------------------------
1 | package kafka
2 |
3 | import (
4 | "context"
5 | "errors"
6 | KafkaGo "github.com/segmentio/kafka-go"
7 | "net"
8 | "strconv"
9 | "wait4it/pkg/model"
10 | )
11 |
12 | func (c *KafkaConnection) BuildContext(cx model.CheckContext) {
13 | c.Port = cx.Port
14 | c.Host = cx.Host
15 | c.ConnectionType = cx.KafkaConf.ConnectionType
16 | c.ConnectToLeaderViaNonLeader = cx.KafkaConf.ConnectToLeaderViaNonLeader
17 | }
18 |
19 | func (c *KafkaConnection) Validate() error {
20 | if len(c.Host) == 0 {
21 | return errors.New("host can't be empty")
22 | }
23 |
24 | if c.Port < 1 || c.Port > 65535 {
25 | return errors.New("invalid port range for Kafka")
26 | }
27 |
28 | return nil
29 | }
30 |
31 | func (c *KafkaConnection) Check(ctx context.Context) (bool, bool, error) {
32 | conn, err := KafkaGo.Dial(c.ConnectionType, net.JoinHostPort(c.Host, strconv.Itoa(c.Port)))
33 | if err != nil {
34 | return false, false, err
35 | }
36 | defer conn.Close()
37 |
38 | // check if the operation is successful, then we can assume this is a valid connection
39 | _, err = conn.ReadPartitions()
40 | if err != nil {
41 | return false, false, err
42 | }
43 |
44 | // this part should work, but I have not yet fully tested it.
45 | if c.ConnectToLeaderViaNonLeader {
46 | controller, err := conn.Controller()
47 | if err != nil {
48 | return false, false, err
49 | }
50 | var connLeader *KafkaGo.Conn
51 | connLeader, err = KafkaGo.Dial(c.ConnectionType, net.JoinHostPort(controller.Host, strconv.Itoa(controller.Port)))
52 | if err != nil {
53 | return false, true, err
54 | }
55 | defer connLeader.Close()
56 | }
57 |
58 | return true, true, nil
59 | }
60 |
--------------------------------------------------------------------------------
/pkg/kafka/structs.go:
--------------------------------------------------------------------------------
1 | package kafka
2 |
3 | import (
4 | "wait4it/pkg/model"
5 | )
6 |
7 | type KafkaConnection struct {
8 | Host string
9 | Port int
10 | ConnectionType string
11 | ConnectToLeaderViaNonLeader bool
12 | }
13 |
14 | // NewChecker creates a new checker
15 | func NewChecker(c *model.CheckContext) (model.CheckInterface, error) {
16 | check := &KafkaConnection{}
17 | check.BuildContext(*c)
18 | if err := check.Validate(); err != nil {
19 | return nil, err
20 | }
21 |
22 | return check, nil
23 | }
24 |
--------------------------------------------------------------------------------
/pkg/memcached/helpers.go:
--------------------------------------------------------------------------------
1 | package memcached
2 |
3 | import (
4 | "strconv"
5 | )
6 |
7 | func (m *MemcachedConnection) BuildConnectionString() string {
8 | return m.Host + ":" + strconv.Itoa(m.Port)
9 | }
10 |
--------------------------------------------------------------------------------
/pkg/memcached/memcached.go:
--------------------------------------------------------------------------------
1 | package memcached
2 |
3 | import (
4 | "context"
5 | "errors"
6 |
7 | "wait4it/pkg/model"
8 |
9 | "github.com/bradfitz/gomemcache/memcache"
10 | )
11 |
12 | func (m *MemcachedConnection) BuildContext(cx model.CheckContext) {
13 | m.Host = cx.Host
14 | m.Port = cx.Port
15 | }
16 |
17 | func (m *MemcachedConnection) Validate() error {
18 | if len(m.Host) == 0 {
19 | return errors.New("Host can't be empty")
20 | }
21 |
22 | if m.Port < 1 || m.Port > 65535 {
23 | return errors.New("Invalid port range for Memcached")
24 | }
25 |
26 | return nil
27 | }
28 |
29 | func (m *MemcachedConnection) Check(_ context.Context) (bool, bool, error) {
30 | // TODO: is it possible to handle ping using context?
31 | mc := memcache.New(m.BuildConnectionString())
32 |
33 | if err := mc.Ping(); err != nil {
34 | return false, false, err
35 | }
36 |
37 | return true, true, nil
38 | }
39 |
--------------------------------------------------------------------------------
/pkg/memcached/structs.go:
--------------------------------------------------------------------------------
1 | package memcached
2 |
3 | import (
4 | "wait4it/pkg/model"
5 | )
6 |
7 | type MemcachedConnection struct {
8 | Host string
9 | Port int
10 | }
11 |
12 | // NewChecker creates a new checker
13 | func NewChecker(c *model.CheckContext) (model.CheckInterface, error) {
14 | check := &MemcachedConnection{}
15 | check.BuildContext(*c)
16 | if err := check.Validate(); err != nil {
17 | return nil, err
18 | }
19 |
20 | return check, nil
21 | }
22 |
--------------------------------------------------------------------------------
/pkg/model/check-context-struct.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | type CheckContext struct {
4 | Config ConfigurationContext
5 | Host string
6 | Port int
7 | Username string
8 | Password string
9 | DatabaseName string
10 | DBConf DatabaseSpecificConf
11 | HttpConf HttpSpecificConf
12 | KafkaConf KafkaConf
13 |
14 | Progress func(string)
15 | }
16 |
17 | type ConfigurationContext struct {
18 | CheckType string
19 | Timeout int
20 | }
21 |
22 | type DatabaseSpecificConf struct {
23 | SSLMode string
24 | OperationMode string
25 | }
26 | type HttpSpecificConf struct {
27 | StatusCode int
28 | Text string
29 | FollowRedirect bool
30 | }
31 |
32 | type KafkaConf struct {
33 | ConnectionType string
34 | ConnectToLeaderViaNonLeader bool
35 | }
36 |
--------------------------------------------------------------------------------
/pkg/model/check-interface.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "context"
5 | )
6 |
7 | // CheckInterface is an interface to handle single check
8 | type CheckInterface interface {
9 | Check(ctx context.Context) (bool, bool, error)
10 | }
11 |
--------------------------------------------------------------------------------
/pkg/mongodb/helpers.go:
--------------------------------------------------------------------------------
1 | package mongodb
2 |
3 | import "strconv"
4 |
5 | func (m *MongoDbConnection) buildConnectionString() string {
6 | if len(m.Username) > 0 {
7 | return "mongodb://" + m.Username + ":" + m.Password + "@" + m.Host + ":" + strconv.Itoa(m.Port)
8 | }
9 |
10 | return "mongodb://" + m.Host + ":" + strconv.Itoa(m.Port)
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/mongodb/mongodb.go:
--------------------------------------------------------------------------------
1 | package mongodb
2 |
3 | import (
4 | "context"
5 | "errors"
6 |
7 | "wait4it/pkg/model"
8 |
9 | "go.mongodb.org/mongo-driver/mongo"
10 | "go.mongodb.org/mongo-driver/mongo/options"
11 | "go.mongodb.org/mongo-driver/mongo/readpref"
12 | )
13 |
14 | const WaitTimeOutSeconds = 2
15 |
16 | func (m *MongoDbConnection) BuildContext(cx model.CheckContext) {
17 | m.Port = cx.Port
18 | m.Host = cx.Host
19 | m.Username = cx.Username
20 | m.Password = cx.Password
21 | }
22 |
23 | func (m *MongoDbConnection) Validate() error {
24 | if len(m.Host) == 0 {
25 | return errors.New("host can't be empty")
26 | }
27 |
28 | if len(m.Username) > 0 && len(m.Password) == 0 {
29 | return errors.New("password can't be empty")
30 | }
31 |
32 | if m.Port < 1 || m.Port > 65535 {
33 | return errors.New("invalid port range for mysql")
34 | }
35 |
36 | return nil
37 | }
38 |
39 | func (m *MongoDbConnection) Check(ctx context.Context) (bool, bool, error) {
40 | client, err := mongo.NewClient(options.Client().ApplyURI(m.buildConnectionString()))
41 | if err != nil {
42 | return false, true, err
43 | }
44 |
45 | err = client.Connect(ctx)
46 | if err != nil {
47 | return false, true, err
48 | }
49 |
50 | err = client.Ping(ctx, readpref.Primary())
51 | if err != nil {
52 | return false, false, err
53 | }
54 |
55 | return true, true, nil
56 | }
57 |
--------------------------------------------------------------------------------
/pkg/mongodb/structs.go:
--------------------------------------------------------------------------------
1 | package mongodb
2 |
3 | import (
4 | "wait4it/pkg/model"
5 | )
6 |
7 | type MongoDbConnection struct {
8 | Host string
9 | Port int
10 | Username string
11 | Password string
12 | }
13 |
14 | // NewChecker creates a new checker
15 | func NewChecker(c *model.CheckContext) (model.CheckInterface, error) {
16 | check := &MongoDbConnection{}
17 | check.BuildContext(*c)
18 | if err := check.Validate(); err != nil {
19 | return nil, err
20 | }
21 |
22 | return check, nil
23 | }
24 |
--------------------------------------------------------------------------------
/pkg/mysql/helpers.go:
--------------------------------------------------------------------------------
1 | package mysql
2 |
3 | import (
4 | "strconv"
5 | )
6 |
7 | func (m MySQLConnection) BuildConnectionString() string {
8 | dsl := ""
9 |
10 | if len(m.Password) == 0 {
11 | dsl = dsl + m.Username
12 | } else {
13 | dsl = dsl + m.Username + ":" + m.Password
14 | }
15 |
16 | dsl = dsl + "@tcp(" + m.Host + ":" + strconv.Itoa(m.Port) + ")/"
17 |
18 | if len(m.DatabaseName) > 0 {
19 | dsl = dsl + m.DatabaseName
20 | }
21 |
22 | return dsl
23 | }
24 |
--------------------------------------------------------------------------------
/pkg/mysql/mysql.go:
--------------------------------------------------------------------------------
1 | package mysql
2 |
3 | import (
4 | "context"
5 | "database/sql"
6 | "errors"
7 | "io/ioutil"
8 | "log"
9 |
10 | "wait4it/pkg/model"
11 |
12 | "github.com/go-sql-driver/mysql"
13 | )
14 |
15 | func (m *MySQLConnection) BuildContext(cx model.CheckContext) {
16 | m.Port = cx.Port
17 | m.Host = cx.Host
18 | m.Username = cx.Username
19 | m.Password = cx.Password
20 | m.DatabaseName = cx.DatabaseName
21 | }
22 |
23 | func (m *MySQLConnection) Validate() error {
24 | if len(m.Host) == 0 || len(m.Username) == 0 {
25 | return errors.New("host or username can't be empty")
26 | }
27 |
28 | if m.Port < 1 || m.Port > 65535 {
29 | return errors.New("invalid port range for mysql")
30 | }
31 |
32 | return nil
33 | }
34 |
35 | func (m *MySQLConnection) Check(ctx context.Context) (bool, bool, error) {
36 | dsl := m.BuildConnectionString()
37 |
38 | db, err := sql.Open("mysql", dsl)
39 | if err != nil {
40 | return false, true, err
41 | }
42 |
43 | err = mysql.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags))
44 | if err != nil {
45 | return false, true, err
46 | }
47 |
48 | err = db.PingContext(ctx)
49 | if err != nil {
50 | // todo: need a logger
51 | return false, false, nil
52 | }
53 | _ = db.Close()
54 |
55 | return true, true, nil
56 | }
57 |
--------------------------------------------------------------------------------
/pkg/mysql/structs.go:
--------------------------------------------------------------------------------
1 | package mysql
2 |
3 | import (
4 | "wait4it/pkg/model"
5 | )
6 |
7 | type MySQLConnection struct {
8 | Host string
9 | Port int
10 | Username string
11 | Password string
12 | DatabaseName string
13 | }
14 |
15 | // NewChecker creates a new checker
16 | func NewChecker(c *model.CheckContext) (model.CheckInterface, error) {
17 | check := &MySQLConnection{}
18 | check.BuildContext(*c)
19 | if err := check.Validate(); err != nil {
20 | return nil, err
21 | }
22 |
23 | return check, nil
24 | }
25 |
--------------------------------------------------------------------------------
/pkg/postgresql/helpers.go:
--------------------------------------------------------------------------------
1 | package postgresql
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // TODO: why this is exported?
8 | func (pq PostgresSQLConnection) BuildConnectionString() string {
9 | dsl := fmt.Sprintf("host=%s port=%d user=%s password=%s sslmode=%s dbname=%s ",
10 | pq.Host, pq.Port, pq.Username, pq.Password, pq.SSLMode, pq.DatabaseName)
11 |
12 | return dsl
13 | }
14 |
--------------------------------------------------------------------------------
/pkg/postgresql/postgresql.go:
--------------------------------------------------------------------------------
1 | package postgresql
2 |
3 | import (
4 | "context"
5 | "database/sql"
6 | "errors"
7 | "wait4it/pkg/model"
8 |
9 | _ "github.com/lib/pq"
10 | )
11 |
12 | func (pq *PostgresSQLConnection) BuildContext(cx model.CheckContext) {
13 | pq.Port = cx.Port
14 | pq.Host = cx.Host
15 | pq.Username = cx.Username
16 | pq.Password = cx.Password
17 | pq.DatabaseName = cx.DatabaseName
18 | if len(cx.DBConf.SSLMode) < 1 {
19 | pq.SSLMode = "disable"
20 | } else {
21 | pq.SSLMode = cx.DBConf.SSLMode
22 | }
23 | }
24 |
25 | func (pq *PostgresSQLConnection) Validate() error {
26 | if len(pq.Host) == 0 || len(pq.Username) == 0 {
27 | return errors.New("host or username can't be empty")
28 | }
29 |
30 | if pq.Port < 1 || pq.Port > 65535 {
31 | return errors.New("invalid port range for PostgresSQL")
32 | }
33 |
34 | return nil
35 | }
36 |
37 | func (pq *PostgresSQLConnection) Check(ctx context.Context) (bool, bool, error) {
38 | dsl := pq.BuildConnectionString()
39 |
40 | db, err := sql.Open("postgres", dsl)
41 | if err != nil {
42 | return false, true, err
43 | }
44 |
45 | err = db.PingContext(ctx)
46 | if err != nil {
47 | return false, false, nil
48 | }
49 | _ = db.Close()
50 |
51 | return true, true, nil
52 | }
53 |
--------------------------------------------------------------------------------
/pkg/postgresql/structs.go:
--------------------------------------------------------------------------------
1 | package postgresql
2 |
3 | import (
4 | "wait4it/pkg/model"
5 | )
6 |
7 | type PostgresSQLConnection struct {
8 | Host string
9 | Port int
10 | Username string
11 | Password string
12 | DatabaseName string
13 | SSLMode string
14 | }
15 |
16 | // NewChecker creates a new checker
17 | func NewChecker(c *model.CheckContext) (model.CheckInterface, error) {
18 | check := &PostgresSQLConnection{}
19 | check.BuildContext(*c)
20 | if err := check.Validate(); err != nil {
21 | return nil, err
22 | }
23 |
24 | return check, nil
25 | }
26 |
--------------------------------------------------------------------------------
/pkg/rabbitmq/rabbitmq.go:
--------------------------------------------------------------------------------
1 | package rabbitmq
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "fmt"
7 | "net"
8 | "time"
9 |
10 | "wait4it/pkg/model"
11 |
12 | amqp "github.com/rabbitmq/amqp091-go"
13 | )
14 |
15 | // RabbitChecker is the struct to check for a RabbitMQ instance
16 | type RabbitChecker struct {
17 | Host string
18 | Port int
19 | Username string
20 | Password string
21 |
22 | conString string
23 | }
24 |
25 | func (rc *RabbitChecker) BuildContext(cx model.CheckContext) {
26 | rc.Host = cx.Host
27 | rc.Port = cx.Port
28 | rc.Username = cx.Username
29 | rc.Password = cx.Password
30 | }
31 |
32 | func (rc *RabbitChecker) Validate() error {
33 | if rc.Host == "" {
34 | return errors.New("Host should not be empty")
35 | }
36 |
37 | if rc.Username == "" {
38 | return errors.New("Username should not be empty")
39 | }
40 |
41 | if rc.Port == 0 {
42 | return errors.New("Port should not be empty")
43 | }
44 |
45 | rc.conString = fmt.Sprintf("amqp://%s:%s@%s:%d/", rc.Username, rc.Password, rc.Host, rc.Port)
46 |
47 | return nil
48 | }
49 |
50 | func (rc *RabbitChecker) Check(ctx context.Context) (bool, bool, error) {
51 | con, err := amqp.DialConfig(rc.conString, amqp.Config{
52 | Heartbeat: time.Second * 10,
53 | Locale: "en_US",
54 | Dial: func(network, addr string) (net.Conn, error) {
55 | var d net.Dialer
56 | conn, err := d.DialContext(ctx, network, addr)
57 | if err != nil {
58 | return nil, err
59 | }
60 |
61 | // Heartbeating hasn't started yet, don't stall forever on a dead server.
62 | // A deadline is set for TLS and AMQP handshaking. After AMQP is established,
63 | // the deadline is cleared in openComplete.
64 | if err := conn.SetDeadline(time.Now().Add(time.Second * 30)); err != nil {
65 | return nil, err
66 | }
67 |
68 | return conn, nil
69 | },
70 | })
71 |
72 | if err != nil {
73 | return false, false, err
74 | }
75 | defer con.Close()
76 |
77 | ch, err := con.Channel()
78 | if err != nil {
79 | return false, false, err
80 | }
81 | defer ch.Close()
82 |
83 | return true, false, nil
84 | }
85 |
86 | // NewChecker creates a new checker
87 | func NewChecker(c *model.CheckContext) (model.CheckInterface, error) {
88 | check := &RabbitChecker{}
89 | check.BuildContext(*c)
90 | if err := check.Validate(); err != nil {
91 | return nil, err
92 | }
93 |
94 | return check, nil
95 | }
96 |
--------------------------------------------------------------------------------
/pkg/redis/helpers.go:
--------------------------------------------------------------------------------
1 | package redis
2 |
3 | import (
4 | "strconv"
5 | )
6 |
7 | func (m RedisConnection) BuildConnectionString() string {
8 | return m.Host + ":" + strconv.Itoa(m.Port)
9 | }
10 |
--------------------------------------------------------------------------------
/pkg/redis/redis.go:
--------------------------------------------------------------------------------
1 | package redis
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "strconv"
7 | "strings"
8 |
9 | "wait4it/pkg/model"
10 |
11 | "github.com/go-redis/redis/v8"
12 | )
13 |
14 | const (
15 | Cluster = "cluster"
16 | Standalone = "standalone"
17 | )
18 |
19 | func (m *RedisConnection) BuildContext(cx model.CheckContext) {
20 | m.Host = cx.Host
21 | m.Port = cx.Port
22 | m.Password = cx.Password
23 |
24 | d, err := strconv.Atoi(cx.DatabaseName)
25 | if err != nil {
26 | d = 0
27 | }
28 | m.Database = d
29 |
30 | switch cx.DBConf.OperationMode {
31 | case Cluster:
32 | m.OperationMode = Cluster
33 | case Standalone:
34 | m.OperationMode = Standalone
35 | default:
36 | m.OperationMode = Standalone
37 | }
38 | }
39 |
40 | func (m *RedisConnection) Validate() error {
41 | if len(m.Host) == 0 {
42 | return errors.New("host or username can't be empty")
43 | }
44 |
45 | if m.OperationMode != Cluster && m.OperationMode != Standalone {
46 | return errors.New("invalid operation mode")
47 | }
48 |
49 | if m.Port < 1 || m.Port > 65535 {
50 | return errors.New("invalid port range for redis")
51 | }
52 |
53 | return nil
54 | }
55 |
56 | func (m *RedisConnection) Check(ctx context.Context) (bool, bool, error) {
57 | switch m.OperationMode {
58 | case Standalone:
59 | return m.checkStandAlone(ctx)
60 | case Cluster:
61 | return m.checkCluster(ctx)
62 | default:
63 | return false, false, nil
64 | }
65 | }
66 |
67 | func (m *RedisConnection) checkStandAlone(ctx context.Context) (bool, bool, error) {
68 | rdb := redis.NewClient(&redis.Options{
69 | Addr: m.BuildConnectionString(),
70 | Password: m.Password, // no password set
71 | DB: m.Database, // use default DB
72 | })
73 |
74 | _, err := rdb.Ping(ctx).Result()
75 | if err != nil {
76 | return false, false, nil
77 | }
78 |
79 | _ = rdb.Close()
80 |
81 | return true, true, nil
82 | }
83 |
84 | func (m *RedisConnection) checkCluster(ctx context.Context) (bool, bool, error) {
85 | rdb := redis.NewClusterClient(&redis.ClusterOptions{
86 | Addrs: []string{m.BuildConnectionString()}, //todo: add support for multiple hosts
87 | Password: m.Password,
88 | })
89 | defer rdb.Close()
90 |
91 | _, err := rdb.Ping(ctx).Result()
92 | if err != nil {
93 | return false, false, nil
94 | }
95 |
96 | result, err := rdb.ClusterInfo(ctx).Result()
97 | if err != nil {
98 | return false, false, nil
99 | }
100 |
101 | if result != "" {
102 | if !strings.Contains(result, "cluster_state:ok") {
103 | return false, false, errors.New("cluster is not healthy")
104 | }
105 | }
106 |
107 | return true, true, nil
108 | }
109 |
--------------------------------------------------------------------------------
/pkg/redis/structs.go:
--------------------------------------------------------------------------------
1 | package redis
2 |
3 | import (
4 | "wait4it/pkg/model"
5 | )
6 |
7 | type RedisConnection struct {
8 | Host string
9 | Port int
10 | Password string
11 | Database int
12 | OperationMode string
13 | }
14 |
15 | // NewChecker creates a new checker
16 | func NewChecker(c *model.CheckContext) (model.CheckInterface, error) {
17 | check := &RedisConnection{}
18 | check.BuildContext(*c)
19 | if err := check.Validate(); err != nil {
20 | return nil, err
21 | }
22 |
23 | return check, nil
24 | }
25 |
--------------------------------------------------------------------------------
/pkg/tcp/helpers.go:
--------------------------------------------------------------------------------
1 | package tcp
2 |
3 | func (tcp *check) isPortInValidRange() bool {
4 | if tcp.port < minPort || tcp.port > maxPort {
5 | return false
6 | }
7 | return true
8 | }
9 |
--------------------------------------------------------------------------------
/pkg/tcp/structs.go:
--------------------------------------------------------------------------------
1 | package tcp
2 |
3 | import (
4 | "wait4it/pkg/model"
5 | )
6 |
7 | const (
8 | minPort = 1
9 | maxPort = 65535
10 | )
11 |
12 | type check struct {
13 | addr string
14 | port int
15 | }
16 |
17 | // NewChecker creates a new checker
18 | func NewChecker(c *model.CheckContext) (model.CheckInterface, error) {
19 | check := &check{
20 | addr: c.Host,
21 | port: c.Port,
22 | }
23 | if err := check.validate(); err != nil {
24 | return nil, err
25 | }
26 |
27 | return check, nil
28 | }
29 |
--------------------------------------------------------------------------------
/pkg/tcp/tcp.go:
--------------------------------------------------------------------------------
1 | package tcp
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "fmt"
7 | "net"
8 | )
9 |
10 | func (tcp *check) validate() error {
11 | if !tcp.isPortInValidRange() {
12 | return errors.New("invalid port range")
13 | }
14 | return nil
15 | }
16 |
17 | func (tcp *check) Check(ctx context.Context) (bool, bool, error) {
18 | var d net.Dialer
19 | c, err := d.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", tcp.addr, tcp.port))
20 | if err != nil {
21 | return false, false, err
22 | }
23 | _ = c.Close()
24 |
25 | return true, false, nil
26 | }
27 |
--------------------------------------------------------------------------------