Compare commits
403 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c15458cd10 | ||
|
|
8fa672edd1 | ||
|
|
3d68a8ddb0 | ||
|
|
c8ded47fce | ||
|
|
f9e7274c55 | ||
|
|
a86b57e326 | ||
|
|
b312df68cf | ||
|
|
590bc364f3 | ||
|
|
5fc4314462 | ||
|
|
a2ed4aa847 | ||
|
|
9ffd00006d | ||
|
|
748ae21788 | ||
|
|
2b3ad7e7c4 | ||
|
|
d07bb785cf | ||
|
|
37fed1c94c | ||
|
|
601d725ff8 | ||
|
|
4914517330 | ||
|
|
bbaff67dbd | ||
|
|
d228cc6dee | ||
|
|
1810764092 | ||
|
|
aaec84a2ae | ||
|
|
6f40303aa3 | ||
|
|
5c4922538a | ||
|
|
156c6abffc | ||
|
|
deaa66117a | ||
|
|
bb63e17fbc | ||
|
|
7806ba76ab | ||
|
|
3d2c892973 | ||
|
|
d2226836a1 | ||
|
|
f510fef4ce | ||
|
|
cb7f2aafda | ||
|
|
c02401e94a | ||
|
|
aec4df9260 | ||
|
|
8928e191cc | ||
|
|
45ba37fa5a | ||
|
|
f20d5f88c5 | ||
|
|
cfc88af2d4 | ||
|
|
9575af7a2b | ||
|
|
6e180f2c29 | ||
|
|
b9e986abc0 | ||
|
|
efaf1fb336 | ||
|
|
0168670bca | ||
|
|
bc96c3e387 | ||
|
|
6473b7f96c | ||
|
|
8fe6bbfa35 | ||
|
|
0ad19f7dde | ||
|
|
fc1e9588ec | ||
|
|
1e63c503ff | ||
|
|
c5384ba086 | ||
|
|
220c2af8bc | ||
|
|
0f8734801b | ||
|
|
e88221b616 | ||
|
|
8fe2013f32 | ||
|
|
f4d893ede1 | ||
|
|
f8bba86158 | ||
|
|
eb86e37521 | ||
|
|
c2d6eb2329 | ||
|
|
e8ac3944a8 | ||
|
|
bbc2dd0cbe | ||
|
|
3babcb3302 | ||
|
|
a062a89401 | ||
|
|
2a71c5a32b | ||
|
|
e379c664f1 | ||
|
|
8b0b1bbeac | ||
|
|
ff093efe14 | ||
|
|
a4a34c624f | ||
|
|
8a021f4afb | ||
|
|
983d92c46f | ||
|
|
da50df986a | ||
|
|
471e7ad875 | ||
|
|
c3efac3bf5 | ||
|
|
a0f87f6fe2 | ||
|
|
87d95b1b37 | ||
|
|
43c2bc65a8 | ||
|
|
312632d55e | ||
|
|
0492869496 | ||
|
|
92905bcd1c | ||
|
|
aa72a7e049 | ||
|
|
68b2d8a11a | ||
|
|
deea6df2fe | ||
|
|
2c81b0ff74 | ||
|
|
53aee9178d | ||
|
|
abbc5d4b22 | ||
|
|
979e06b437 | ||
|
|
b1d7ff77e9 | ||
|
|
666f21d5af | ||
|
|
ff98621150 | ||
|
|
45c0e0b532 | ||
|
|
8544e497a5 | ||
|
|
5dcbeaad15 | ||
|
|
c124937ce9 | ||
|
|
6b6643c077 | ||
|
|
f90030d081 | ||
|
|
3d70c52f40 | ||
|
|
24bae7b645 | ||
|
|
8888aec7fc | ||
|
|
ac2bbea915 | ||
|
|
3ee936d6e7 | ||
|
|
7b61e7b047 | ||
|
|
e6156a9142 | ||
|
|
670282f19f | ||
|
|
3c1226db5b | ||
|
|
e1d5d96df1 | ||
|
|
5d8c2a697a | ||
|
|
ece93e239a | ||
|
|
d8624693cd | ||
|
|
882d759828 | ||
|
|
e365603bcd | ||
|
|
22b198a06d | ||
|
|
c2be62af10 | ||
|
|
87b8acea1c | ||
|
|
35c587e08e | ||
|
|
7195bab2ee | ||
|
|
980ea3fe6a | ||
|
|
c931574615 | ||
|
|
59e784c7ed | ||
|
|
439f97f1e1 | ||
|
|
7acec88b03 | ||
|
|
d22a4dff72 | ||
|
|
82570ce986 | ||
|
|
6388d7d3fc | ||
|
|
ee301949a9 | ||
|
|
3a4be02bc9 | ||
|
|
ecb9594bd0 | ||
|
|
29caf91d79 | ||
|
|
2574114d3e | ||
|
|
4097c3e90e | ||
|
|
d98e841bc8 | ||
|
|
af86681f16 | ||
|
|
5f228e4bdc | ||
|
|
177ca6e6f9 | ||
|
|
772b8a4f2d | ||
|
|
8bcaf051e2 | ||
|
|
a6258bbdfb | ||
|
|
52e73534c7 | ||
|
|
6c4996905a | ||
|
|
7dc8076840 | ||
|
|
19ce70ba1c | ||
|
|
f026e7291c | ||
|
|
129b5a75b2 | ||
|
|
d354c2b751 | ||
|
|
bae3ecca7b | ||
|
|
a7241b6fbe | ||
|
|
e66f3f5a59 | ||
|
|
237032a714 | ||
|
|
fb622085ce | ||
|
|
b5a653e305 | ||
|
|
41ee1c569a | ||
|
|
d48b4a262f | ||
|
|
64cdc9457d | ||
|
|
1cf1121856 | ||
|
|
dd2a732525 | ||
|
|
67cf6734ff | ||
|
|
abfd6753da | ||
|
|
65d1ea047b | ||
|
|
cc3fd3fe67 | ||
|
|
acc39b3097 | ||
|
|
773e736123 | ||
|
|
2414ef7424 | ||
|
|
1ab60cba59 | ||
|
|
78685301fb | ||
|
|
841f000c75 | ||
|
|
caa83c17d9 | ||
|
|
0a17e06d8f | ||
|
|
d3650f33a3 | ||
|
|
77c2d7f0c5 | ||
|
|
9821a78847 | ||
|
|
a3cd0a5743 | ||
|
|
fd0091fb4c | ||
|
|
fc34a9dd08 | ||
|
|
747dba19b7 | ||
|
|
6e826b08f3 | ||
|
|
922b8ca832 | ||
|
|
175015ce9e | ||
|
|
ed13c83b0e | ||
|
|
e1a6681f5f | ||
|
|
e14b340f00 | ||
|
|
b465f58a74 | ||
|
|
ea62473ab8 | ||
|
|
3f924fc5ad | ||
|
|
cc9dda416b | ||
|
|
d31a1ccea9 | ||
|
|
a86a86a4ba | ||
|
|
f2706e4fc6 | ||
|
|
ed3672cb54 | ||
|
|
14373f5123 | ||
|
|
5154002cb2 | ||
|
|
9d0f8cd63a | ||
|
|
b8fa7a591f | ||
|
|
6a912b2784 | ||
|
|
5755224c2a | ||
|
|
676a80038b | ||
|
|
3eafab1e0b | ||
|
|
f66a9c14c7 | ||
|
|
1906a86374 | ||
|
|
b29bb29654 | ||
|
|
fa7912f829 | ||
|
|
42f4f67087 | ||
|
|
1e4763c450 | ||
|
|
ca20f7c0a3 | ||
|
|
825491fb06 | ||
|
|
40ebe10b93 | ||
|
|
60186225a7 | ||
|
|
dbeb53198b | ||
|
|
08a6f0edeb | ||
|
|
f3bc71a07b | ||
|
|
4736d96752 | ||
|
|
9c0832a8b5 | ||
|
|
1c302cfac7 | ||
|
|
d6c7e90311 | ||
|
|
456e6fa087 | ||
|
|
ce8186cc15 | ||
|
|
17a9a6777a | ||
|
|
0752bb9a10 | ||
|
|
0360950eec | ||
|
|
99f7e0d980 | ||
|
|
b04e950358 | ||
|
|
328bc80c14 | ||
|
|
203e6ab780 | ||
|
|
2f467ff83e | ||
|
|
e9234a827c | ||
|
|
b773e61c69 | ||
|
|
898b5379da | ||
|
|
68833e4786 | ||
|
|
6a26c4ecf0 | ||
|
|
6118cda6de | ||
|
|
d5bac298ec | ||
|
|
2286bc65a7 | ||
|
|
254faa7145 | ||
|
|
a927ba66a5 | ||
|
|
6c9b6b7768 | ||
|
|
77abe748f0 | ||
|
|
6e17e178da | ||
|
|
55f4cff699 | ||
|
|
6ed4dcc86a | ||
|
|
fad25076f7 | ||
|
|
6c796ecd2c | ||
|
|
df9c0f6863 | ||
|
|
6be5d7e42d | ||
|
|
69dd53d669 | ||
|
|
3673ea9b72 | ||
|
|
0a4f53b7c9 | ||
|
|
f5a84c249a | ||
|
|
63a9f24193 | ||
|
|
fa37625f36 | ||
|
|
a73fda2a29 | ||
|
|
75c80bdc96 | ||
|
|
2414a65371 | ||
|
|
83ca9b0a89 | ||
|
|
6bceaee801 | ||
|
|
70dfa399e4 | ||
|
|
8e89aa7a28 | ||
|
|
9a1938c071 | ||
|
|
7e150daac7 | ||
|
|
3386dd3af9 | ||
|
|
293a03153c | ||
|
|
092fa34e1a | ||
|
|
20d42bf83c | ||
|
|
a7ee8c6061 | ||
|
|
1afcedc3e3 | ||
|
|
8e49c76039 | ||
|
|
c9599676c9 | ||
|
|
d547a4ca81 | ||
|
|
f2764fad0c | ||
|
|
cf381dfc1a | ||
|
|
0806405099 | ||
|
|
d4f2b50a5e | ||
|
|
584166f80d | ||
|
|
23f5d5e4e7 | ||
|
|
57d0c70ad7 | ||
|
|
d3e02e24c5 | ||
|
|
9fd2f387c5 | ||
|
|
2f71ef6063 | ||
|
|
1a7416d5c7 | ||
|
|
eec14bbe18 | ||
|
|
9a9997d5e0 | ||
|
|
2c041bc351 | ||
|
|
8f98c28b96 | ||
|
|
da3071cfa0 | ||
|
|
5484947ee3 | ||
|
|
c0f31b2ac5 | ||
|
|
d39c0e65aa | ||
|
|
d090da9dcf | ||
|
|
2ea63a2609 | ||
|
|
5766f7d1c5 | ||
|
|
0b762f6b88 | ||
|
|
51e708742d | ||
|
|
99d74aefa4 | ||
|
|
0105f6d1a4 | ||
|
|
9168739140 | ||
|
|
429c82c372 | ||
|
|
4f65f43267 | ||
|
|
bae86f4956 | ||
|
|
e0f550374f | ||
|
|
2dc9b9d921 | ||
|
|
bb11e88e26 | ||
|
|
63392b9aab | ||
|
|
6fe24d3bbf | ||
|
|
340f4d9baa | ||
|
|
aa37ca02b5 | ||
|
|
34b3b7e1e3 | ||
|
|
11067205e9 | ||
|
|
753e909411 | ||
|
|
14d2ae53bc | ||
|
|
93ab440dd9 | ||
|
|
612cf9408f | ||
|
|
da6287d060 | ||
|
|
b84b5a2d8d | ||
|
|
b219c984c4 | ||
|
|
b11dc46d7b | ||
|
|
bff0f0a5bb | ||
|
|
61637599d8 | ||
|
|
d9642eec3f | ||
|
|
d277b4e878 | ||
|
|
e84c9c874b | ||
|
|
682822eef7 | ||
|
|
9fcbcadba7 | ||
|
|
198ba36dbd | ||
|
|
3a48f9f194 | ||
|
|
df0b584c8e | ||
|
|
d794ca1c7f | ||
|
|
00c143a8c8 | ||
|
|
c2d8f330af | ||
|
|
c5d89dc981 | ||
|
|
784bc40e76 | ||
|
|
74e13abded | ||
|
|
2519bf2815 | ||
|
|
7b0477755d | ||
|
|
a6cec39b16 | ||
|
|
277648b4ff | ||
|
|
b76f0aa3f8 | ||
|
|
5a35458586 | ||
|
|
fb0bec2950 | ||
|
|
7af36da76e | ||
|
|
4017da77fb | ||
|
|
6f336bbcae | ||
|
|
6db4c9ba65 | ||
|
|
8fb35f9ff6 | ||
|
|
e41e34d45c | ||
|
|
cb82e5d221 | ||
|
|
23aba02e41 | ||
|
|
c281818579 | ||
|
|
42a8a9af80 | ||
|
|
33e86ac18e | ||
|
|
b4dae48a5e | ||
|
|
8b9704bc69 | ||
|
|
def504ca04 | ||
|
|
451dbefc93 | ||
|
|
1e35ffb1fe | ||
|
|
570b0246b7 | ||
|
|
b91360220c | ||
|
|
1c82ba87ba | ||
|
|
14e64b8460 | ||
|
|
314549c038 | ||
|
|
df059f7e63 | ||
|
|
2a620d8c6e | ||
|
|
ba0de92f84 | ||
|
|
38fd3dd372 | ||
|
|
14b5eac802 | ||
|
|
25fc2710fc | ||
|
|
54fff1e191 | ||
|
|
0f7a940e11 | ||
|
|
b53895dead | ||
|
|
d0cb1cad44 | ||
|
|
ebdd78d6b0 | ||
|
|
3f5be54938 | ||
|
|
fca8ef26c5 | ||
|
|
aa12682a42 | ||
|
|
bfa3714634 | ||
|
|
a7c3ee024b | ||
|
|
63eb64cbde | ||
|
|
fd58b5c248 | ||
|
|
126d121e34 | ||
|
|
30331275f6 | ||
|
|
8571dddd98 | ||
|
|
cf6f3945b5 | ||
|
|
f644f7b9b3 | ||
|
|
2e656e8882 | ||
|
|
a277b7c00e | ||
|
|
26d7d1620e | ||
|
|
e7b65efc7a | ||
|
|
3884eb9648 | ||
|
|
c1a02644b6 | ||
|
|
63135ded8a | ||
|
|
ded48ab821 | ||
|
|
c014837e41 | ||
|
|
c0768d7843 | ||
|
|
32a6fe1d91 | ||
|
|
6187483bc2 | ||
|
|
59143841c9 | ||
|
|
f9f22dbdf2 | ||
|
|
3da3a319af | ||
|
|
2d5a9a2b42 | ||
|
|
50d34442b7 | ||
|
|
d52a4d86e9 | ||
|
|
0b18c00db3 | ||
|
|
561c8372da | ||
|
|
db32fa51aa | ||
|
|
6a28fd9474 | ||
|
|
d52961e06e | ||
|
|
969728eb69 | ||
|
|
5001340f0d | ||
|
|
7d21f2d5db |
5
.babelrc
@@ -1,9 +1,8 @@
|
|||||||
{
|
{
|
||||||
"presets": [["@babel/preset-env", { "modules": false }]],
|
"presets": [["env", { "modules": false }]],
|
||||||
"plugins": [["@babel/plugin-transform-runtime", { "regenerator": true }]],
|
|
||||||
"env": {
|
"env": {
|
||||||
"test": {
|
"test": {
|
||||||
"presets": [["@babel/preset-env", { "targets": { "node": "current" } }]]
|
"presets": [["env", { "targets": { "node": "current" } }]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: amir20
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Desktop (please complete the following information):**
|
||||||
|
- OS: [e.g. iOS, Windows or Mac Os]
|
||||||
|
- Docker version [e.g. `docker version`'s output]
|
||||||
|
- Browser & version [e.g. chrome, safari]
|
||||||
|
- Version [e.g. 1.26.1. Can be found at `<Dozzle-host>/version`]
|
||||||
|
|
||||||
|
**If applicable include logs with `--level debug` and browser logs**
|
||||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: enhancement
|
||||||
|
assignees: amir20
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
0
demo.gif → .github/demo.gif
vendored
|
Before Width: | Height: | Size: 24 MiB After Width: | Height: | Size: 24 MiB |
17
.github/stale.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Number of days of inactivity before an issue becomes stale
|
||||||
|
daysUntilStale: 20
|
||||||
|
# Number of days of inactivity before a stale issue is closed
|
||||||
|
daysUntilClose: 3
|
||||||
|
# Issues with these labels will never be considered stale
|
||||||
|
exemptLabels:
|
||||||
|
- pinned
|
||||||
|
- security
|
||||||
|
# Label to use when marking an issue as stale
|
||||||
|
staleLabel: wontfix
|
||||||
|
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||||
|
markComment: >
|
||||||
|
This issue has been automatically marked as stale because it has not had
|
||||||
|
recent activity. It will be closed if no further activity occurs. Thank you
|
||||||
|
for your contributions.
|
||||||
|
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||||
|
closeComment: false
|
||||||
30
.github/workflows/deploy.yml
vendored
@@ -5,17 +5,19 @@ on:
|
|||||||
name: Test and Release
|
name: Test and Release
|
||||||
jobs:
|
jobs:
|
||||||
npm-test:
|
npm-test:
|
||||||
name: npm test
|
name: JavaScript Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: npm test
|
- name: Install Node
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
- name: npm it
|
- name: Install depdencies
|
||||||
run: npm it
|
run: yarn
|
||||||
|
- name: Run Tests
|
||||||
|
run: yarn test
|
||||||
go-test:
|
go-test:
|
||||||
name: go test
|
name: Go Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
@@ -24,10 +26,21 @@ jobs:
|
|||||||
go-version: 1.14.x
|
go-version: 1.14.x
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Test
|
- name: Run Go Tests with Coverage
|
||||||
run: go test -cover ./...
|
run: go test -cover ./...
|
||||||
buildx:
|
int-test:
|
||||||
needs: [go-test, npm-test]
|
needs: [go-test, npm-test]
|
||||||
|
name: Integration Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Build images
|
||||||
|
run: docker-compose -f integration/docker-compose.test.yml build
|
||||||
|
- name: Run tests
|
||||||
|
run: docker-compose -f integration/docker-compose.test.yml run integration
|
||||||
|
buildx:
|
||||||
|
needs: [int-test]
|
||||||
name: Release
|
name: Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -37,7 +50,8 @@ jobs:
|
|||||||
id: buildx
|
id: buildx
|
||||||
uses: crazy-max/ghaction-docker-buildx@v1
|
uses: crazy-max/ghaction-docker-buildx@v1
|
||||||
with:
|
with:
|
||||||
version: latest
|
buildx-version: latest
|
||||||
|
qemu-version: latest
|
||||||
- name: Available platforms
|
- name: Available platforms
|
||||||
run: echo ${{ steps.buildx.outputs.platforms }}
|
run: echo ${{ steps.buildx.outputs.platforms }}
|
||||||
- name: Docker Login
|
- name: Docker Login
|
||||||
|
|||||||
@@ -2,17 +2,19 @@ on: push
|
|||||||
name: Test
|
name: Test
|
||||||
jobs:
|
jobs:
|
||||||
npm-test:
|
npm-test:
|
||||||
name: npm test
|
name: JavaScript Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Install Node
|
- name: Install Node
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
- name: Run NPM Tests
|
- name: Install depdencies
|
||||||
run: npm it
|
run: yarn
|
||||||
|
- name: Run Tests
|
||||||
|
run: yarn test
|
||||||
go-test:
|
go-test:
|
||||||
name: go test
|
name: Go Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
@@ -21,13 +23,15 @@ jobs:
|
|||||||
go-version: 1.14.x
|
go-version: 1.14.x
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Run Go Tests
|
- name: Run Go Tests with Coverage
|
||||||
run: go test -cover ./...
|
run: go test -cover ./...
|
||||||
docker-build:
|
int-test:
|
||||||
name: Integration Tests
|
name: Integration Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
- name: Build images
|
||||||
|
run: docker-compose -f integration/docker-compose.test.yml build
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: docker-compose -f integration/docker-compose.test.yml run integration
|
run: docker-compose -f integration/docker-compose.test.yml run integration
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
minifySvg: false,
|
|
||||||
};
|
|
||||||
2
.reflex
@@ -1 +1 @@
|
|||||||
-r '\.go$' -R '^node_modules/' -R '^static/' -R '^.cache/' -G '*_test.go' -s -- go run main.go --level debug
|
-r '\.go$' -R '^node_modules/' -R '^static/' -R '^.cache/' -G '*_test.go' -s -- go run main.go routes.go --level debug
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build assets
|
# Build assets
|
||||||
FROM node:13-alpine as node
|
FROM node:current-alpine as node
|
||||||
|
|
||||||
RUN apk add --no-cache git openssh python make g++ util-linux
|
RUN apk add --no-cache git openssh python make g++ util-linux
|
||||||
|
|
||||||
@@ -10,16 +10,14 @@ COPY package*.json yarn.lock ./
|
|||||||
RUN yarn install --network-timeout 1000000
|
RUN yarn install --network-timeout 1000000
|
||||||
|
|
||||||
# Copy config files
|
# Copy config files
|
||||||
COPY .* ./
|
COPY .* webpack*.js ./
|
||||||
|
|
||||||
# Copy assets to build
|
# Copy assets to build
|
||||||
COPY assets ./assets
|
COPY assets ./assets
|
||||||
|
|
||||||
|
|
||||||
# Do the build
|
# Do the build
|
||||||
RUN yarn build
|
RUN yarn build
|
||||||
|
|
||||||
|
|
||||||
FROM golang:1.14-alpine AS builder
|
FROM golang:1.14-alpine AS builder
|
||||||
|
|
||||||
RUN apk add --no-cache git ca-certificates
|
RUN apk add --no-cache git ca-certificates
|
||||||
@@ -52,7 +50,6 @@ RUN CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=$TAG" -o dozzle
|
|||||||
FROM scratch
|
FROM scratch
|
||||||
|
|
||||||
ENV PATH=/bin
|
ENV PATH=/bin
|
||||||
ENV DOCKER_API_VERSION 1.38
|
|
||||||
|
|
||||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||||
COPY --from=builder /dozzle/dozzle /dozzle
|
COPY --from=builder /dozzle/dozzle /dozzle
|
||||||
|
|||||||
2
Makefile
@@ -1,5 +1,5 @@
|
|||||||
TAG := $(shell git describe --tags)
|
TAG := $(shell git describe --tags)
|
||||||
PLATFROMS := linux/amd64,linux/arm64,linux/arm/v7
|
PLATFROMS := linux/amd64,linux/arm/v7,linux/arm64/v8
|
||||||
|
|
||||||
.PHONY: publish
|
.PHONY: publish
|
||||||
publish:
|
publish:
|
||||||
|
|||||||
53
README.md
@@ -6,13 +6,13 @@
|
|||||||
|
|
||||||
# Dozzle - [dozzle.dev](https://dozzle.dev/)
|
# Dozzle - [dozzle.dev](https://dozzle.dev/)
|
||||||
|
|
||||||
Dozzle is a real-time log viewer for Docker. It's free. It's small. And it's in your browser.
|
Dozzle is a simple, lightweight application that provides you with a web based interface to monitor your Docker container logs live. It doesn’t store log information, it is for live monitoring of your container logs only.
|
||||||
|
|
||||||
While dozzle should work for most, it is not meant to be a full logging solution. For enterprise use, I recommend you look at [Loggly](https://www.loggly.com), [Papertrail](https://papertrailapp.com) or [Kibana](https://www.elastic.co/products/kibana).
|
While dozzle should work for most, it is not meant to be a full logging solution. For enterprise applications, products like [Loggly](https://www.loggly.com), [Papertrail](https://papertrailapp.com) or [Kibana](https://www.elastic.co/products/kibana) are more suited.
|
||||||
|
|
||||||
But if you don't want to pay for these services, then Dozzle can help! Dozzle will be able to capture all logs from your containers and send them in real-time to your browser. Installation is also very easy. Dozzle is not a database. It does not store or save any logs. You can only see live logs while using Dozzle.
|
Dozzle doesn't cost any money. Dozzle aims to stay simple, small and free.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Getting dozzle
|
## Getting dozzle
|
||||||
|
|
||||||
@@ -81,11 +81,51 @@ Dozzle follows the [12-factor](https://12factor.net/) model. Configurations can
|
|||||||
| `--addr` | `DOZZLE_ADDR` | `:8080` |
|
| `--addr` | `DOZZLE_ADDR` | `:8080` |
|
||||||
| `--base` | `DOZZLE_BASE` | `/` |
|
| `--base` | `DOZZLE_BASE` | `/` |
|
||||||
| `--level` | `DOZZLE_LEVEL` | `info` |
|
| `--level` | `DOZZLE_LEVEL` | `info` |
|
||||||
| `--showAll` | `DOZZLE_SHOWALL` | `false` |
|
| n/a | `DOCKER_API_VERSION` | not set |
|
||||||
| n/a | `DOCKER_API_VERSION` | `1.38` |
|
|
||||||
| `--tailSize` | `DOZZLE_TAILSIZE` | `300` |
|
| `--tailSize` | `DOZZLE_TAILSIZE` | `300` |
|
||||||
| `--filter` | `DOZZLE_FILTER` | `""` |
|
| `--filter` | `DOZZLE_FILTER` | `""` |
|
||||||
|
|
||||||
|
## Troubleshooting and FAQs
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>I installed Dozzle, but logs are slow or they never load. Help!</summary>
|
||||||
|
|
||||||
|
Dozzle uses Server Sent Events (SSE) which connects to a sever using a HTTP stream without closing the connection. If any proxy tries to buffer this connection, then Dozzle never receives the data and hangs forever waiting for the reverse proxy to flush the buffer. Since version `1.23.0`, Dozzle send the `X-Accel-Buffering: no` header which should stop reverse proxies buffering. However, some proxies may ignore this header. In those case, you need to explicitly disable any buffering.
|
||||||
|
|
||||||
|
Below is an example with nginx and using `proxy_pass` to disable buffering.
|
||||||
|
|
||||||
|
```
|
||||||
|
server {
|
||||||
|
...
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://<dozzle.container.ip.address>:8080;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api {
|
||||||
|
proxy_pass http://<dozzle.container.ip.address>:8080;
|
||||||
|
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_cache off;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>What data does Dozzle collect?</summary>
|
||||||
|
|
||||||
|
Dozzle does not collect any metrics or analytics. Dozzle has a [strict](https://github.com/amir20/dozzle/blob/master/routes.go#L33-L38) Content Security Policy which only allows the following policies:
|
||||||
|
|
||||||
|
- Allow connect to `api.github.com` to fetch most recent version.
|
||||||
|
- Allow fonts from `fonts.gstatic.com` and styles from `fonts.googleapis.com`
|
||||||
|
- Only allow `<script>` and `<style>` files from `self`
|
||||||
|
|
||||||
|
Dozzle opens all links with `rel="noopener"`.
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[MIT](LICENSE)
|
[MIT](LICENSE)
|
||||||
@@ -101,4 +141,3 @@ To Build and test locally:
|
|||||||
5. Install node modules with `npm install`.
|
5. Install node modules with `npm install`.
|
||||||
6. Do `npm start`
|
6. Do `npm start`
|
||||||
|
|
||||||
Instructions for Github actions can be found [here](.github/goreleaser/Dockerfile) which build and tests Dozzle.
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/* snapshot: Test_createRoutes_foobar */
|
/* snapshot: Test_createRoutes_foobar */
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Connection: close
|
Connection: close
|
||||||
|
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline' fonts.googleapis.com; img-src 'self'; manifest-src 'self'; font-src fonts.gstatic.com; connect-src 'self' api.github.com; require-trusted-types-for 'script'
|
||||||
Content-Type: text/plain; charset=utf-8
|
Content-Type: text/plain; charset=utf-8
|
||||||
|
|
||||||
foo page
|
foo page
|
||||||
@@ -8,6 +9,7 @@ foo page
|
|||||||
/* snapshot: Test_createRoutes_index */
|
/* snapshot: Test_createRoutes_index */
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Connection: close
|
Connection: close
|
||||||
|
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline' fonts.googleapis.com; img-src 'self'; manifest-src 'self'; font-src fonts.gstatic.com; connect-src 'self' api.github.com; require-trusted-types-for 'script'
|
||||||
Content-Type: text/plain; charset=utf-8
|
Content-Type: text/plain; charset=utf-8
|
||||||
|
|
||||||
index page
|
index page
|
||||||
@@ -15,6 +17,7 @@ index page
|
|||||||
/* snapshot: Test_createRoutes_redirect */
|
/* snapshot: Test_createRoutes_redirect */
|
||||||
HTTP/1.1 301 Moved Permanently
|
HTTP/1.1 301 Moved Permanently
|
||||||
Connection: close
|
Connection: close
|
||||||
|
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline' fonts.googleapis.com; img-src 'self'; manifest-src 'self'; font-src fonts.gstatic.com; connect-src 'self' api.github.com; require-trusted-types-for 'script'
|
||||||
Content-Type: text/html; charset=utf-8
|
Content-Type: text/html; charset=utf-8
|
||||||
Location: /foobar/
|
Location: /foobar/
|
||||||
|
|
||||||
@@ -23,6 +26,7 @@ Location: /foobar/
|
|||||||
/* snapshot: Test_createRoutes_version */
|
/* snapshot: Test_createRoutes_version */
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Connection: close
|
Connection: close
|
||||||
|
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline' fonts.googleapis.com; img-src 'self'; manifest-src 'self'; font-src fonts.gstatic.com; connect-src 'self' api.github.com; require-trusted-types-for 'script'
|
||||||
Content-Type: text/plain; charset=utf-8
|
Content-Type: text/plain; charset=utf-8
|
||||||
|
|
||||||
dev
|
dev
|
||||||
@@ -39,14 +43,16 @@ HTTP/1.1 200 OK
|
|||||||
Connection: close
|
Connection: close
|
||||||
Cache-Control: no-cache
|
Cache-Control: no-cache
|
||||||
Connection: keep-alive
|
Connection: keep-alive
|
||||||
Content-Type: text/event-stream
|
Content-Type: text/event-stream
|
||||||
|
X-Accel-Buffering: no
|
||||||
|
|
||||||
/* snapshot: Test_handler_streamEvents_error_request */
|
/* snapshot: Test_handler_streamEvents_error_request */
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Connection: close
|
Connection: close
|
||||||
Cache-Control: no-cache
|
Cache-Control: no-cache
|
||||||
Connection: keep-alive
|
Connection: keep-alive
|
||||||
Content-Type: text/event-stream
|
Content-Type: text/event-stream
|
||||||
|
X-Accel-Buffering: no
|
||||||
|
|
||||||
/* snapshot: Test_handler_streamEvents_happy */
|
/* snapshot: Test_handler_streamEvents_happy */
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
@@ -54,12 +60,13 @@ Connection: close
|
|||||||
Cache-Control: no-cache
|
Cache-Control: no-cache
|
||||||
Connection: keep-alive
|
Connection: keep-alive
|
||||||
Content-Type: text/event-stream
|
Content-Type: text/event-stream
|
||||||
|
X-Accel-Buffering: no
|
||||||
|
|
||||||
event: containers-changed
|
event: containers-changed
|
||||||
data: start
|
data: start
|
||||||
|
|
||||||
/* snapshot: Test_handler_streamLogs_error_finding_container */
|
/* snapshot: Test_handler_streamLogs_error_finding_container */
|
||||||
HTTP/1.1 500 Internal Server Error
|
HTTP/1.1 404 Not Found
|
||||||
Connection: close
|
Connection: close
|
||||||
Content-Type: text/plain; charset=utf-8
|
Content-Type: text/plain; charset=utf-8
|
||||||
X-Content-Type-Options: nosniff
|
X-Content-Type-Options: nosniff
|
||||||
@@ -71,7 +78,8 @@ HTTP/1.1 200 OK
|
|||||||
Connection: close
|
Connection: close
|
||||||
Cache-Control: no-cache
|
Cache-Control: no-cache
|
||||||
Connection: keep-alive
|
Connection: keep-alive
|
||||||
Content-Type: text/event-stream
|
Content-Type: text/event-stream
|
||||||
|
X-Accel-Buffering: no
|
||||||
|
|
||||||
/* snapshot: Test_handler_streamLogs_happy */
|
/* snapshot: Test_handler_streamLogs_happy */
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
@@ -79,5 +87,28 @@ Connection: close
|
|||||||
Cache-Control: no-cache
|
Cache-Control: no-cache
|
||||||
Connection: keep-alive
|
Connection: keep-alive
|
||||||
Content-Type: text/event-stream
|
Content-Type: text/event-stream
|
||||||
|
X-Accel-Buffering: no
|
||||||
|
|
||||||
data: INFO Testing logs...
|
data: INFO Testing logs...
|
||||||
|
|
||||||
|
/* snapshot: Test_handler_streamLogs_happy_container_stopped */
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Connection: close
|
||||||
|
Cache-Control: no-cache
|
||||||
|
Connection: keep-alive
|
||||||
|
Content-Type: text/event-stream
|
||||||
|
X-Accel-Buffering: no
|
||||||
|
|
||||||
|
event: container-stopped
|
||||||
|
data: end of stream
|
||||||
|
|
||||||
|
/* snapshot: Test_handler_streamLogs_happy_with_id */
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Connection: close
|
||||||
|
Cache-Control: no-cache
|
||||||
|
Connection: keep-alive
|
||||||
|
Content-Type: text/event-stream
|
||||||
|
X-Accel-Buffering: no
|
||||||
|
|
||||||
|
data: 2020-05-13T18:55:37.772853839Z INFO Testing logs...
|
||||||
|
id: 2020-05-13T18:55:37.772853839Z
|
||||||
@@ -3,6 +3,8 @@ import { shallowMount, RouterLinkStub, createLocalVue } from "@vue/test-utils";
|
|||||||
import Vuex from "vuex";
|
import Vuex from "vuex";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
|
|
||||||
|
jest.mock("./store/config.js", () => ({ base: "" }));
|
||||||
|
|
||||||
const localVue = createLocalVue();
|
const localVue = createLocalVue();
|
||||||
|
|
||||||
localVue.use(Vuex);
|
localVue.use(Vuex);
|
||||||
@@ -12,31 +14,31 @@ describe("<App />", () => {
|
|||||||
let store;
|
let store;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
global.BASE_PATH = "";
|
|
||||||
global.EventSource = EventSource;
|
global.EventSource = EventSource;
|
||||||
const state = {
|
const state = {
|
||||||
containers: [
|
|
||||||
{ id: "abc", name: "Test 1" },
|
|
||||||
{ id: "xyz", name: "Test 2" },
|
|
||||||
],
|
|
||||||
settings: { menuWidth: 15 },
|
settings: { menuWidth: 15 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getters = {
|
||||||
|
visibleContainers() {
|
||||||
|
return [
|
||||||
|
{ id: "abc", name: "Test 1" },
|
||||||
|
{ id: "xyz", name: "Test 2" },
|
||||||
|
];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
FETCH_CONTAINERS: () => Promise.resolve(),
|
FETCH_CONTAINERS: () => Promise.resolve(),
|
||||||
};
|
};
|
||||||
|
|
||||||
store = new Vuex.Store({
|
store = new Vuex.Store({
|
||||||
state,
|
state,
|
||||||
|
getters,
|
||||||
actions,
|
actions,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("is a Vue instance", async () => {
|
|
||||||
const wrapper = shallowMount(App, { stubs, store, localVue });
|
|
||||||
expect(wrapper.isVueInstance()).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("has right title", async () => {
|
test("has right title", async () => {
|
||||||
const wrapper = shallowMount(App, { stubs, store, localVue });
|
const wrapper = shallowMount(App, { stubs, store, localVue });
|
||||||
await wrapper.vm.$nextTick();
|
await wrapper.vm.$nextTick();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template lang="html">
|
<template>
|
||||||
<main>
|
<main>
|
||||||
<mobile-menu v-if="isMobile"></mobile-menu>
|
<mobile-menu v-if="isMobile"></mobile-menu>
|
||||||
|
|
||||||
@@ -76,12 +76,15 @@ export default {
|
|||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
await this.fetchContainerList();
|
await this.fetchContainerList();
|
||||||
this.title = `${this.containers.length} containers`;
|
this.title = `${this.visibleContainers.length} containers`;
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.hasSmallerScrollbars) {
|
if (this.hasSmallerScrollbars) {
|
||||||
document.documentElement.classList.add("has-custom-scrollbars");
|
document.documentElement.classList.add("has-custom-scrollbars");
|
||||||
}
|
}
|
||||||
|
if (this.hasLightTheme) {
|
||||||
|
document.documentElement.setAttribute("data-theme", "light");
|
||||||
|
}
|
||||||
this.menuWidth = this.settings.menuWidth;
|
this.menuWidth = this.settings.menuWidth;
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -92,12 +95,23 @@ export default {
|
|||||||
document.documentElement.classList.remove("has-custom-scrollbars");
|
document.documentElement.classList.remove("has-custom-scrollbars");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
hasLightTheme(newValue, oldValue) {
|
||||||
|
if (newValue) {
|
||||||
|
document.documentElement.setAttribute("data-theme", "light");
|
||||||
|
} else {
|
||||||
|
document.documentElement.removeAttribute("data-theme");
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["containers", "activeContainers", "isMobile", "settings"]),
|
...mapState(["activeContainers", "isMobile", "settings"]),
|
||||||
|
...mapGetters(["visibleContainers"]),
|
||||||
hasSmallerScrollbars() {
|
hasSmallerScrollbars() {
|
||||||
return this.settings.smallerScrollbars;
|
return this.settings.smallerScrollbars;
|
||||||
},
|
},
|
||||||
|
hasLightTheme() {
|
||||||
|
return this.settings.lightTheme;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
@@ -116,11 +130,11 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
::v-deep .splitpanes__splitter {
|
::v-deep .splitpanes--vertical > .splitpanes__splitter {
|
||||||
min-width: 4px;
|
min-width: 3px;
|
||||||
background: #666;
|
background: var(--border-color);
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgb(255, 221, 87);
|
background: var(--border-hover-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template lang="html">
|
<template>
|
||||||
<div class="name columns is-marginless">
|
<div class="name columns is-marginless">
|
||||||
<span class="column">{{ value }}</span>
|
<span class="column">{{ value }}</span>
|
||||||
<span class="column is-narrow" v-if="closable">
|
<span class="column is-narrow" v-if="closable">
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
<template>
|
<template functional>
|
||||||
<svg class="icomoon" :class="['icon-' + name]">
|
<svg class="icomoon" :class="['icon-' + props.name]">
|
||||||
<use :href="'#icon-' + name"></use>
|
<use :href="'#icon-' + props.name"></use>
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
functional: true,
|
|
||||||
props: {
|
props: {
|
||||||
name: {
|
name: {
|
||||||
required: true,
|
required: true,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template lang="html">
|
<template>
|
||||||
<div ref="observer" class="control" :class="{ 'is-loading': isLoading }"></div>
|
<div ref="observer" class="control" :class="{ 'is-loading': isLoading }"></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -31,6 +31,8 @@ export default {
|
|||||||
);
|
);
|
||||||
|
|
||||||
intersectionObserver.observe(this.$refs.observer);
|
intersectionObserver.observe(this.$refs.observer);
|
||||||
|
|
||||||
|
this.$once("hook:beforeDestroy", () => intersectionObserver.disconnect());
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import EventSource from "eventsourcemock";
|
|||||||
import { sources } from "eventsourcemock";
|
import { sources } from "eventsourcemock";
|
||||||
import { shallowMount, mount, createLocalVue } from "@vue/test-utils";
|
import { shallowMount, mount, createLocalVue } from "@vue/test-utils";
|
||||||
import Vuex from "vuex";
|
import Vuex from "vuex";
|
||||||
import MockDate from "mockdate";
|
|
||||||
import LogEventSource from "./LogEventSource.vue";
|
import LogEventSource from "./LogEventSource.vue";
|
||||||
import LogViewer from "./LogViewer.vue";
|
import LogViewer from "./LogViewer.vue";
|
||||||
|
|
||||||
@@ -13,31 +12,28 @@ jest.mock("lodash.debounce", () =>
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
jest.mock("../store/config.js", () => ({ base: "" }));
|
||||||
|
|
||||||
describe("<LogEventSource />", () => {
|
describe("<LogEventSource />", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
global.BASE_PATH = "";
|
|
||||||
global.EventSource = EventSource;
|
global.EventSource = EventSource;
|
||||||
MockDate.set("6/12/2019", 0);
|
|
||||||
window.scrollTo = jest.fn();
|
window.scrollTo = jest.fn();
|
||||||
|
|
||||||
const observe = jest.fn();
|
const observe = jest.fn();
|
||||||
const unobserve = jest.fn();
|
const disconnect = jest.fn();
|
||||||
global.IntersectionObserver = jest.fn(() => ({
|
global.IntersectionObserver = jest.fn(() => ({
|
||||||
observe,
|
observe,
|
||||||
unobserve,
|
disconnect,
|
||||||
}));
|
}));
|
||||||
debounce.mockClear();
|
debounce.mockClear();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => MockDate.reset());
|
|
||||||
|
|
||||||
function createLogEventSource(searchFilter = null) {
|
function createLogEventSource(searchFilter = null) {
|
||||||
const localVue = createLocalVue();
|
const localVue = createLocalVue();
|
||||||
localVue.use(Vuex);
|
localVue.use(Vuex);
|
||||||
|
|
||||||
localVue.component("log-viewer", LogViewer);
|
localVue.component("log-viewer", LogViewer);
|
||||||
|
|
||||||
const state = { searchFilter, settings: { size: "medium" } };
|
const state = { searchFilter, settings: { size: "medium", showTimestamp: true } };
|
||||||
|
|
||||||
const store = new Vuex.Store({
|
const store = new Vuex.Store({
|
||||||
state,
|
state,
|
||||||
@@ -55,24 +51,9 @@ describe("<LogEventSource />", () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
test("is a Vue instance", async () => {
|
|
||||||
const wrapper = shallowMount(LogEventSource);
|
|
||||||
expect(wrapper.isVueInstance()).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("renders correctly", async () => {
|
test("renders correctly", async () => {
|
||||||
const wrapper = createLogEventSource();
|
const wrapper = createLogEventSource();
|
||||||
expect(wrapper.element).toMatchInlineSnapshot(`
|
expect(wrapper.element).toMatchSnapshot();
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
class="control"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ul
|
|
||||||
class="events medium"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should connect to EventSource", async () => {
|
test("should connect to EventSource", async () => {
|
||||||
@@ -105,11 +86,28 @@ describe("<LogEventSource />", () => {
|
|||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("should parse messages with loki's timestamp format", async () => {
|
||||||
|
const wrapper = createLogEventSource();
|
||||||
|
sources["/api/logs/stream?id=abc"].emitOpen();
|
||||||
|
sources["/api/logs/stream?id=abc"].emitMessage({ data: `2020-04-27T12:35:43.272974324+02:00 xxxxx` });
|
||||||
|
|
||||||
|
const [message, _] = wrapper.vm.messages;
|
||||||
|
const { key, ...messageWithoutKey } = message;
|
||||||
|
|
||||||
|
expect(key).toBe("2020-04-27T12:35:43.272974324+02:00");
|
||||||
|
expect(messageWithoutKey).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"date": 2020-04-27T10:35:43.272Z,
|
||||||
|
"message": "xxxxx",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
test("should pass messages to slot", async () => {
|
test("should pass messages to slot", async () => {
|
||||||
const wrapper = createLogEventSource();
|
const wrapper = createLogEventSource();
|
||||||
sources["/api/logs/stream?id=abc"].emitOpen();
|
sources["/api/logs/stream?id=abc"].emitOpen();
|
||||||
sources["/api/logs/stream?id=abc"].emitMessage({ data: `2019-06-12T10:55:42.459034602Z "This is a message."` });
|
sources["/api/logs/stream?id=abc"].emitMessage({ data: `2019-06-12T10:55:42.459034602Z "This is a message."` });
|
||||||
const [message, _] = wrapper.find(LogViewer).vm.messages;
|
const [message, _] = wrapper.findComponent(LogViewer).vm.messages;
|
||||||
|
|
||||||
const { key, ...messageWithoutKey } = message;
|
const { key, ...messageWithoutKey } = message;
|
||||||
|
|
||||||
@@ -123,64 +121,80 @@ describe("<LogEventSource />", () => {
|
|||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should render messages", async () => {
|
describe("render html correctly", () => {
|
||||||
const wrapper = createLogEventSource();
|
const RealDate = Date;
|
||||||
sources["/api/logs/stream?id=abc"].emitOpen();
|
beforeAll(() => {
|
||||||
sources["/api/logs/stream?id=abc"].emitMessage({ data: `2019-06-12T10:55:42.459034602Z "This is a message."` });
|
global.Date = class extends RealDate {
|
||||||
|
constructor(arg) {
|
||||||
|
if (arg) {
|
||||||
|
return new RealDate(arg);
|
||||||
|
} else {
|
||||||
|
return new RealDate(1560336936000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
afterAll(() => (global.Date = RealDate));
|
||||||
|
|
||||||
await wrapper.vm.$nextTick();
|
test("should render messages", async () => {
|
||||||
expect(wrapper.find("ul.events")).toMatchInlineSnapshot(`
|
const wrapper = createLogEventSource();
|
||||||
<ul class="events medium">
|
sources["/api/logs/stream?id=abc"].emitOpen();
|
||||||
<li><span class="date">today at 10:55 AM</span> <span class="text">"This is a message."</span></li>
|
sources["/api/logs/stream?id=abc"].emitMessage({ data: `2019-06-12T10:55:42.459034602Z "This is a message."` });
|
||||||
</ul>
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should render messages with color", async () => {
|
await wrapper.vm.$nextTick();
|
||||||
const wrapper = createLogEventSource();
|
expect(wrapper.find("ul.events")).toMatchInlineSnapshot(`
|
||||||
sources["/api/logs/stream?id=abc"].emitOpen();
|
<ul class="events medium">
|
||||||
sources["/api/logs/stream?id=abc"].emitMessage({
|
<li class=""><span class="date"><time datetime="2019-06-12T10:55:42.459Z">today at 10:55 AM</time></span> <span class="text">"This is a message."</span></li>
|
||||||
data: `2019-06-12T10:55:42.459034602Z \x1b[30mblack\x1b[37mwhite`,
|
</ul>
|
||||||
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
await wrapper.vm.$nextTick();
|
test("should render messages with color", async () => {
|
||||||
expect(wrapper.find("ul.events")).toMatchInlineSnapshot(`
|
const wrapper = createLogEventSource();
|
||||||
<ul class="events medium">
|
sources["/api/logs/stream?id=abc"].emitOpen();
|
||||||
<li><span class="date">today at 10:55 AM</span> <span class="text"><span style="color:#000">black<span style="color:#AAA">white</span></span></span></li>
|
sources["/api/logs/stream?id=abc"].emitMessage({
|
||||||
</ul>
|
data: `2019-06-12T10:55:42.459034602Z \x1b[30mblack\x1b[37mwhite`,
|
||||||
`);
|
});
|
||||||
});
|
|
||||||
|
|
||||||
test("should render messages with html entities", async () => {
|
await wrapper.vm.$nextTick();
|
||||||
const wrapper = createLogEventSource();
|
expect(wrapper.find("ul.events")).toMatchInlineSnapshot(`
|
||||||
sources["/api/logs/stream?id=abc"].emitOpen();
|
<ul class="events medium">
|
||||||
sources["/api/logs/stream?id=abc"].emitMessage({
|
<li class=""><span class="date"><time datetime="2019-06-12T10:55:42.459Z">today at 10:55 AM</time></span> <span class="text"><span style="color:#000">black<span style="color:#AAA">white</span></span></span></li>
|
||||||
data: `2019-06-12T10:55:42.459034602Z <test>foo bar</test>`,
|
</ul>
|
||||||
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
await wrapper.vm.$nextTick();
|
test("should render messages with html entities", async () => {
|
||||||
expect(wrapper.find("ul.events")).toMatchInlineSnapshot(`
|
const wrapper = createLogEventSource();
|
||||||
<ul class="events medium">
|
sources["/api/logs/stream?id=abc"].emitOpen();
|
||||||
<li><span class="date">today at 10:55 AM</span> <span class="text"><test>foo bar</test></span></li>
|
sources["/api/logs/stream?id=abc"].emitMessage({
|
||||||
</ul>
|
data: `2019-06-12T10:55:42.459034602Z <test>foo bar</test>`,
|
||||||
`);
|
});
|
||||||
});
|
|
||||||
|
|
||||||
test("should render messages with filter", async () => {
|
await wrapper.vm.$nextTick();
|
||||||
const wrapper = createLogEventSource("test");
|
expect(wrapper.find("ul.events")).toMatchInlineSnapshot(`
|
||||||
sources["/api/logs/stream?id=abc"].emitOpen();
|
<ul class="events medium">
|
||||||
sources["/api/logs/stream?id=abc"].emitMessage({
|
<li class=""><span class="date"><time datetime="2019-06-12T10:55:42.459Z">today at 10:55 AM</time></span> <span class="text"><test>foo bar</test></span></li>
|
||||||
data: `2019-06-11T10:55:42.459034602Z Foo bar`,
|
</ul>
|
||||||
});
|
`);
|
||||||
sources["/api/logs/stream?id=abc"].emitMessage({
|
|
||||||
data: `2019-06-12T10:55:42.459034602Z This is a test <hi></hi>`,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await wrapper.vm.$nextTick();
|
test("should render messages with filter", async () => {
|
||||||
expect(wrapper.find("ul.events")).toMatchInlineSnapshot(`
|
const wrapper = createLogEventSource("test");
|
||||||
<ul class="events medium">
|
sources["/api/logs/stream?id=abc"].emitOpen();
|
||||||
<li><span class="date">today at 10:55 AM</span> <span class="text">This is a <mark>test</mark> <hi></hi></span></li>
|
sources["/api/logs/stream?id=abc"].emitMessage({
|
||||||
</ul>
|
data: `2019-06-11T10:55:42.459034602Z Foo bar`,
|
||||||
`);
|
});
|
||||||
|
sources["/api/logs/stream?id=abc"].emitMessage({
|
||||||
|
data: `2019-06-12T10:55:42.459034602Z This is a test <hi></hi>`,
|
||||||
|
});
|
||||||
|
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
|
expect(wrapper.find("ul.events")).toMatchInlineSnapshot(`
|
||||||
|
<ul class="events medium">
|
||||||
|
<li class=""><span class="date"><time datetime="2019-06-12T10:55:42.459Z">today at 10:55 AM</time></span> <span class="text">This is a <mark>test</mark> <hi></hi></span></li>
|
||||||
|
</ul>
|
||||||
|
`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template lang="html">
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<infinite-loader :onLoadMore="loadOlderLogs" :enabled="messages.length > 100"></infinite-loader>
|
<infinite-loader :onLoadMore="loadOlderLogs" :enabled="messages.length > 100"></infinite-loader>
|
||||||
<slot :messages="messages"></slot>
|
<slot :messages="messages"></slot>
|
||||||
@@ -8,17 +8,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import debounce from "lodash.debounce";
|
import debounce from "lodash.debounce";
|
||||||
import InfiniteLoader from "./InfiniteLoader";
|
import InfiniteLoader from "./InfiniteLoader";
|
||||||
|
import config from "../store/config";
|
||||||
function parseMessage(data) {
|
|
||||||
const date = new Date(data.substring(0, 30));
|
|
||||||
const key = data.substring(0, 30);
|
|
||||||
const message = data.substring(30).trim();
|
|
||||||
return {
|
|
||||||
key,
|
|
||||||
date,
|
|
||||||
message,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ["id"],
|
props: ["id"],
|
||||||
@@ -41,22 +31,27 @@ export default {
|
|||||||
if (this.es) {
|
if (this.es) {
|
||||||
this.es.close();
|
this.es.close();
|
||||||
this.messages = [];
|
this.messages = [];
|
||||||
|
this.buffer = [];
|
||||||
this.es = null;
|
this.es = null;
|
||||||
}
|
}
|
||||||
this.es = new EventSource(`${BASE_PATH}/api/logs/stream?id=${this.id}`);
|
this.es = new EventSource(`${config.base}/api/logs/stream?id=${this.id}`);
|
||||||
const flushBuffer = debounce(
|
|
||||||
() => {
|
this.es.addEventListener("container-stopped", (e) => {
|
||||||
this.messages.push(...this.buffer);
|
this.es.close();
|
||||||
this.buffer = [];
|
this.buffer.push({ event: "container-stopped", message: "Container stopped", date: new Date() });
|
||||||
},
|
flushNow();
|
||||||
250,
|
});
|
||||||
{ maxWait: 1000 }
|
this.es.addEventListener("error", (e) => console.log("EventSource failed: " + JSON.stringify(e)));
|
||||||
);
|
|
||||||
|
const flushBuffer = debounce(() => flushNow(), 250, { maxWait: 1000 });
|
||||||
|
const flushNow = () => {
|
||||||
|
this.messages.push(...this.buffer);
|
||||||
|
this.buffer = [];
|
||||||
|
};
|
||||||
this.es.onmessage = (e) => {
|
this.es.onmessage = (e) => {
|
||||||
this.buffer.push(parseMessage(e.data));
|
this.buffer.push(this.parseMessage(e.data));
|
||||||
flushBuffer();
|
flushBuffer();
|
||||||
};
|
};
|
||||||
this.es.onerror = (e) => console.log("EventSource failed." + e);
|
|
||||||
this.$once("hook:beforeDestroy", () => this.es.close());
|
this.$once("hook:beforeDestroy", () => this.es.close());
|
||||||
},
|
},
|
||||||
async loadOlderLogs() {
|
async loadOlderLogs() {
|
||||||
@@ -73,10 +68,20 @@ export default {
|
|||||||
const newMessages = logs
|
const newMessages = logs
|
||||||
.trim()
|
.trim()
|
||||||
.split("\n")
|
.split("\n")
|
||||||
.map((line) => parseMessage(line));
|
.map((line) => this.parseMessage(line));
|
||||||
this.messages.unshift(...newMessages);
|
this.messages.unshift(...newMessages);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
parseMessage(data) {
|
||||||
|
let i = data.indexOf(" ");
|
||||||
|
if (i == -1) {
|
||||||
|
i = data.length;
|
||||||
|
}
|
||||||
|
const key = data.substring(0, i);
|
||||||
|
const date = new Date(key);
|
||||||
|
const message = data.substring(i).trim();
|
||||||
|
return { key, date, message };
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
id(newValue, oldValue) {
|
id(newValue, oldValue) {
|
||||||
|
|||||||
@@ -1,23 +1,29 @@
|
|||||||
<template lang="html">
|
<template>
|
||||||
<ul class="events" :class="settings.size">
|
<ul class="events" :class="settings.size">
|
||||||
<li v-for="item in filtered" :key="item.key">
|
<li v-for="item in filtered" :key="item.key" :class="{ event: !!item.event }">
|
||||||
<span class="date">{{ item.date | relativeTime }}</span>
|
<span class="date" v-if="settings.showTimestamp"><relative-time :date="item.date"></relative-time></span>
|
||||||
<span class="text" v-html="colorize(item.message)"></span>
|
<span class="text" v-html="colorize(item.message)"></span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapActions, mapGetters, mapState } from "vuex";
|
import { mapActions, mapGetters, mapState } from "vuex";
|
||||||
import { formatRelative } from "date-fns";
|
|
||||||
import AnsiConvertor from "ansi-to-html";
|
import AnsiConvertor from "ansi-to-html";
|
||||||
|
import DOMPurify from "dompurify";
|
||||||
|
import RelativeTime from "./RelativeTime";
|
||||||
|
|
||||||
const ansiConvertor = new AnsiConvertor({ escapeXML: true });
|
const ansiConvertor = new AnsiConvertor({ escapeXML: true });
|
||||||
|
|
||||||
|
if (window.trustedTypes && trustedTypes.createPolicy) {
|
||||||
|
trustedTypes.createPolicy("default", {
|
||||||
|
createHTML: (string, sink) => DOMPurify.sanitize(string, { RETURN_TRUSTED_TYPE: true }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ["messages"],
|
props: ["messages"],
|
||||||
name: "LogViewer",
|
name: "LogViewer",
|
||||||
components: {},
|
components: { RelativeTime },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showSearch: false,
|
showSearch: false,
|
||||||
@@ -53,11 +59,6 @@ export default {
|
|||||||
return messages;
|
return messages;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
filters: {
|
|
||||||
relativeTime(date) {
|
|
||||||
return formatRelative(date, new Date());
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -68,6 +69,10 @@ export default {
|
|||||||
& > li {
|
& > li {
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
line-height: 130%;
|
line-height: 130%;
|
||||||
|
&:last-child {
|
||||||
|
scroll-snap-align: end;
|
||||||
|
scroll-margin-block-end: 5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.small {
|
&.small {
|
||||||
@@ -86,16 +91,27 @@ export default {
|
|||||||
.date {
|
.date {
|
||||||
background-color: #262626;
|
background-color: #262626;
|
||||||
color: #258ccd;
|
color: #258ccd;
|
||||||
|
|
||||||
|
[data-theme="light"] & {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
color: #009900;
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
li.event {
|
||||||
|
color: #f14668;
|
||||||
|
}
|
||||||
|
|
||||||
::v-deep mark {
|
::v-deep mark {
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
background-color: #ffdd57;
|
background-color: var(--secondary-color);
|
||||||
animation: pops 0.2s ease-out;
|
animation: pops 200ms ease-out;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template lang="html">
|
<template>
|
||||||
<log-event-source :id="id" v-slot="eventSource">
|
<log-event-source :id="id" v-slot="eventSource">
|
||||||
<log-viewer :messages="eventSource.messages"></log-viewer>
|
<log-viewer :messages="eventSource.messages"></log-viewer>
|
||||||
</log-event-source>
|
</log-event-source>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template lang="html">
|
<template>
|
||||||
<aside>
|
<aside>
|
||||||
<a
|
<a
|
||||||
role="button"
|
role="button"
|
||||||
class="navbar-burger burger is-white is-hidden-tablet is-pulled-right"
|
class="navbar-burger burger is-hidden-tablet is-pulled-right"
|
||||||
@click="showNav = !showNav"
|
@click="showNav = !showNav"
|
||||||
:class="{ 'is-active': showNav }"
|
:class="{ 'is-active': showNav }"
|
||||||
>
|
>
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
<h1 class="title has-text-warning is-marginless">Dozzle</h1>
|
<h1 class="title has-text-warning is-marginless">Dozzle</h1>
|
||||||
<p class="menu-label is-hidden-mobile" :class="{ 'is-active': showNav }">Containers</p>
|
<p class="menu-label is-hidden-mobile" :class="{ 'is-active': showNav }">Containers</p>
|
||||||
<ul class="menu-list is-hidden-mobile" :class="{ 'is-active': showNav }">
|
<ul class="menu-list is-hidden-mobile" :class="{ 'is-active': showNav }">
|
||||||
<li v-for="item in containers">
|
<li v-for="item in visibleContainers" :key="item.id">
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'container', params: { id: item.id, name: item.name } }"
|
:to="{ name: 'container', params: { id: item.id, name: item.name } }"
|
||||||
active-class="is-active"
|
active-class="is-active"
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapActions, mapGetters, mapState } from "vuex";
|
import { mapGetters } from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: [],
|
props: [],
|
||||||
@@ -37,13 +37,8 @@ export default {
|
|||||||
showNav: false,
|
showNav: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["containers"]),
|
...mapGetters(["activeContainersById", "visibleContainers"]),
|
||||||
...mapGetters(["activeContainersById"]),
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions({}),
|
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
$route(to, from) {
|
$route(to, from) {
|
||||||
@@ -59,7 +54,7 @@ aside {
|
|||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
background: #222;
|
background: var(--scheme-main-ter);
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
max-height: 100vh;
|
max-height: 100vh;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@@ -73,8 +68,12 @@ aside {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.burger.is-white {
|
.title {
|
||||||
color: #fff;
|
text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.burger {
|
||||||
|
color: var(--body-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-hidden-mobile.is-active {
|
.is-hidden-mobile.is-active {
|
||||||
|
|||||||
34
assets/components/RelativeTime.vue
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<time :datetime="date.toISOString()">{{ date | relativeTime }}</time>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { formatRelative } from "date-fns";
|
||||||
|
import { enGB, enUS } from "date-fns/locale";
|
||||||
|
|
||||||
|
const use24Hr =
|
||||||
|
new Intl.DateTimeFormat(undefined, {
|
||||||
|
hour: "numeric",
|
||||||
|
})
|
||||||
|
.formatToParts(new Date(2020, 0, 1, 13))
|
||||||
|
.find((part) => part.type === "hour").value.length === 2;
|
||||||
|
|
||||||
|
const locale = use24Hr ? enGB : enUS;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
date: {
|
||||||
|
required: true,
|
||||||
|
type: Date,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
name: "RelativeTime",
|
||||||
|
components: {},
|
||||||
|
|
||||||
|
filters: {
|
||||||
|
relativeTime(date) {
|
||||||
|
return formatRelative(date, new Date(), { locale });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
96
assets/components/ScrollProgress.vue
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<template>
|
||||||
|
<div class="scroll-progress">
|
||||||
|
<svg width="100" height="100" viewBox="0 0 100 100">
|
||||||
|
<circle r="44" cx="50" cy="50" :style="{ '--progress': scrollProgress }" />
|
||||||
|
</svg>
|
||||||
|
<div class="percent columns is-vcentered is-centered has-text-weight-light">
|
||||||
|
<span class="column is-narrow is-paddingless is-size-2">
|
||||||
|
{{ Math.ceil(scrollProgress * 100) }}
|
||||||
|
</span>
|
||||||
|
<span class="column is-narrow is-paddingless">
|
||||||
|
%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from "vuex";
|
||||||
|
import throttle from "lodash.throttle";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ScrollProgress",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
scrollProgress: 0,
|
||||||
|
animation: { cancel: () => {} },
|
||||||
|
parentElement: document,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.onScrollThrottled = throttle(this.onScroll, 150);
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.attachEvents();
|
||||||
|
this.$once("hook:beforeDestroy", this.detachEvents);
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
activeContainers() {
|
||||||
|
this.detachEvents();
|
||||||
|
this.attachEvents();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(["activeContainers"]),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
attachEvents() {
|
||||||
|
this.parentElement = this.$el.closest("[data-scrolling]") || document;
|
||||||
|
this.parentElement.addEventListener("scroll", this.onScrollThrottled);
|
||||||
|
},
|
||||||
|
detachEvents() {
|
||||||
|
this.parentElement.removeEventListener("scroll", this.onScrollThrottled);
|
||||||
|
},
|
||||||
|
onScroll() {
|
||||||
|
const p = this.parentElement == document ? document.documentElement : this.parentElement;
|
||||||
|
this.scrollProgress = p.scrollTop / (p.scrollHeight - p.clientHeight);
|
||||||
|
this.animation.cancel();
|
||||||
|
this.animation = this.$el.animate(
|
||||||
|
{ opacity: [1, 0] },
|
||||||
|
{
|
||||||
|
duration: 500,
|
||||||
|
delay: 2000,
|
||||||
|
fill: "both",
|
||||||
|
easing: "ease-out",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.scroll-progress {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
circle {
|
||||||
|
fill: var(--scheme-main-ter);
|
||||||
|
fill-opacity: 0.8;
|
||||||
|
transition: stroke-dashoffset 250ms ease-out;
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
stroke: var(--primary-color);
|
||||||
|
stroke-dashoffset: calc(276.32px - var(--progress) * 276.32px);
|
||||||
|
stroke-dasharray: 276.32px 276.32px;
|
||||||
|
stroke-width: 3;
|
||||||
|
will-change: stroke-dashoffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.percent {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,20 +1,19 @@
|
|||||||
<template lang="html">
|
<template>
|
||||||
<section :class="{ 'is-full-height-scrollable': scrollable }">
|
<section :class="{ 'is-full-height-scrollable': scrollable }">
|
||||||
<header v-if="$slots.header">
|
<header v-if="$slots.header">
|
||||||
<slot name="header"></slot>
|
<slot name="header"></slot>
|
||||||
</header>
|
</header>
|
||||||
<main ref="content" :data-scrolling="scrollable">
|
<main ref="content" :data-scrolling="scrollable">
|
||||||
|
<div class="scrollbar-progress is-hidden-mobile">
|
||||||
|
<scroll-progress v-show="paused"></scroll-progress>
|
||||||
|
</div>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<div ref="scrollObserver"></div>
|
<div ref="scrollObserver"></div>
|
||||||
</main>
|
</main>
|
||||||
<div class="scroll-bar-notification">
|
|
||||||
|
<div class="scrollbar-notification">
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<button
|
<button class="button" :class="hasMore ? 'has-more' : ''" @click="scrollToBottom('instant')" v-show="paused">
|
||||||
class="button"
|
|
||||||
:class="hasMore ? 'is-warning' : 'is-primary'"
|
|
||||||
@click="scrollToBottom('instant')"
|
|
||||||
v-show="paused"
|
|
||||||
>
|
|
||||||
<icon name="download"></icon>
|
<icon name="download"></icon>
|
||||||
</button>
|
</button>
|
||||||
</transition>
|
</transition>
|
||||||
@@ -24,6 +23,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
|
import ScrollProgress from "./ScrollProgress";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@@ -34,6 +34,7 @@ export default {
|
|||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Icon,
|
Icon,
|
||||||
|
ScrollProgress,
|
||||||
},
|
},
|
||||||
name: "ScrollableView",
|
name: "ScrollableView",
|
||||||
data() {
|
data() {
|
||||||
@@ -44,20 +45,22 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
const { content } = this.$refs;
|
const { content } = this.$refs;
|
||||||
new MutationObserver((e) => {
|
const mutationObserver = new MutationObserver((e) => {
|
||||||
if (!this.paused) {
|
if (!this.paused) {
|
||||||
this.scrollToBottom("instant");
|
this.scrollToBottom("instant");
|
||||||
} else {
|
} else {
|
||||||
this.hasMore = true;
|
this.hasMore = true;
|
||||||
}
|
}
|
||||||
}).observe(content, { childList: true, subtree: true });
|
});
|
||||||
|
mutationObserver.observe(content, { childList: true, subtree: true });
|
||||||
|
this.$once("hook:beforeDestroy", () => mutationObserver.disconnect());
|
||||||
|
|
||||||
const intersectionObserver = new IntersectionObserver(
|
const intersectionObserver = new IntersectionObserver(
|
||||||
(entries) => (this.paused = entries[0].intersectionRatio == 0),
|
(entries) => (this.paused = entries[0].intersectionRatio == 0),
|
||||||
{ threshholds: [0, 1], rootMargin: "80px 0px" }
|
{ threshholds: [0, 1], rootMargin: "80px 0px" }
|
||||||
);
|
);
|
||||||
|
|
||||||
intersectionObserver.observe(this.$refs.scrollObserver);
|
intersectionObserver.observe(this.$refs.scrollObserver);
|
||||||
|
this.$once("hook:beforeDestroy", () => intersectionObserver.disconnect());
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@@ -80,14 +83,53 @@ section {
|
|||||||
main {
|
main {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
scroll-snap-type: y proximity;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-bar-notification {
|
.scrollbar-progress {
|
||||||
|
text-align: right;
|
||||||
|
margin-right: 110px;
|
||||||
|
.scroll-progress {
|
||||||
|
position: fixed;
|
||||||
|
top: 60px;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollbar-notification {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
margin-right: 65px;
|
margin-right: 65px;
|
||||||
button {
|
button {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 30px;
|
bottom: 30px;
|
||||||
|
background-color: var(--secondary-color);
|
||||||
|
transition: background-color 1s ease-out;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
||||||
|
border: none !important;
|
||||||
|
|
||||||
|
&.has-more {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
animation-name: bounce;
|
||||||
|
animation-duration: 1000ms;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce {
|
||||||
|
0%,
|
||||||
|
20%,
|
||||||
|
50%,
|
||||||
|
80%,
|
||||||
|
100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
transform: translateY(-30px);
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
transform: translateY(-15px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template lang="html">
|
<template>
|
||||||
<div class="search columns is-gapless is-vcentered" v-show="showSearch" v-if="settings.search">
|
<div class="search columns is-gapless is-vcentered" v-show="showSearch" v-if="settings.search">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<p class="control has-icons-left">
|
<p class="control has-icons-left">
|
||||||
@@ -75,17 +75,26 @@ export default {
|
|||||||
width: 350px;
|
width: 350px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background: rgba(50, 50, 50, 0.9);
|
background: var(--scheme-main-ter);
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
border-radius: 0 0 0 5px;
|
border-radius: 0 0 0 5px;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
||||||
.delete {
|
|
||||||
margin-left: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
.delete {
|
||||||
padding: 10px 3px;
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
padding: 10px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
color: var(--body-color);
|
||||||
|
&::placeholder {
|
||||||
|
color: var(--border-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template lang="html">
|
<template>
|
||||||
<aside>
|
<aside>
|
||||||
<div class="columns is-marginless">
|
<div class="columns is-marginless">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<h1 class="title has-text-warning is-marginless">Dozzle</h1>
|
<h1 class="title has-text-warning is-marginless">Dozzle</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-narrow has-text-right is-hidden-mobile">
|
<div class="column is-narrow has-text-right x">
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'settings' }"
|
:to="{ name: 'settings' }"
|
||||||
active-class="is-active"
|
active-class="is-active"
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="menu-label is-hidden-mobile">Containers</p>
|
<p class="menu-label is-hidden-mobile">Containers</p>
|
||||||
<ul class="menu-list is-hidden-mobile">
|
<ul class="menu-list is-hidden-mobile">
|
||||||
<li v-for="item in containers">
|
<li v-for="item in visibleContainers" :key="item.id" :class="item.state">
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'container', params: { id: item.id, name: item.name } }"
|
:to="{ name: 'container', params: { id: item.id, name: item.name } }"
|
||||||
active-class="is-active"
|
active-class="is-active"
|
||||||
@@ -55,8 +55,8 @@ export default {
|
|||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["containers", "activeContainers"]),
|
...mapState(["activeContainers"]),
|
||||||
...mapGetters(["activeContainersById"]),
|
...mapGetters(["activeContainersById", "visibleContainers"]),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
@@ -79,20 +79,25 @@ aside {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.burger.is-white {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-hidden-mobile.is-active {
|
.is-hidden-mobile.is-active {
|
||||||
display: block !important;
|
display: block !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1.title {
|
||||||
|
font-family: "Gafata", sans-serif;
|
||||||
|
text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
li.exited a {
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
|
||||||
.will-append-container.icon {
|
.will-append-container.icon {
|
||||||
transition: transform 0.2s ease-out;
|
transition: transform 0.2s ease-out;
|
||||||
&.is-active {
|
&.is-active {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
color: #00d1b2;
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
.router-link-exact-active & {
|
.router-link-exact-active & {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
|
|||||||
13
assets/components/__snapshots__/LogEventSource.spec.js.snap
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`<LogEventSource /> renders correctly 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="control"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ul
|
||||||
|
class="events medium"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
Before Width: | Height: | Size: 88 KiB |
5
assets/favicon.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="128" height="128" rx="6" fill="#222222"/>
|
||||||
|
<path d="M82.3248 94.3863H123V104.093H67.8025V95.3506L106.164 44.3736H68.3808V34.5382H121.072V42.9594L82.3248 94.3863Z" fill="#FFDD57"/>
|
||||||
|
<path d="M8 107.107L17.5656 14L43.8372 16.7013C51.9339 17.5338 58.9091 20.0604 64.7629 24.2812C70.6166 28.5019 74.8873 34.0893 77.5749 41.0432C80.3052 48.0016 81.2514 55.7674 80.4137 64.3407L79.8027 70.2877C78.9005 79.0698 76.4053 86.5894 72.3173 92.8468C68.2719 99.1084 62.914 103.684 56.2436 106.574C49.6158 109.468 42.1213 110.529 33.7602 109.755L8 107.107ZM28.8005 25.3655L21.3043 98.3288L34.2164 99.6565C43.6767 100.629 51.3299 98.4435 57.1758 93.0993C63.0644 87.7595 66.5671 79.6542 67.684 68.7832L68.2424 63.3477C69.3286 52.7752 67.6788 44.3123 63.293 37.9592C58.9542 31.5678 52.2295 27.8607 43.1188 26.8377L28.8005 25.3655Z" fill="#FFDD57"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 949 B |
@@ -4,20 +4,21 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>Dozzle</title>
|
<title>Dozzle</title>
|
||||||
<link href="https://fonts.googleapis.com/css?family=Roboto|Roboto+Mono|Gafata" rel="stylesheet" />
|
<link
|
||||||
<link rel="manifest" href="manifest.webmanifest" />
|
href="https://fonts.googleapis.com/css2?family=Gafata&family=Roboto:wght@300;400&family=Roboto+Mono&display=swap"
|
||||||
<link href="styles.scss" rel="stylesheet" />
|
rel="stylesheet"
|
||||||
<link rel="icon" href="favicon.ico" />
|
/>
|
||||||
<script>
|
<script type="application/json" id="config__json">
|
||||||
window["BASE_PATH"] = "{{ .Base }}";
|
{
|
||||||
window["VERSION"] = "{{ .Version }}";
|
"base": "{{ .Base }}",
|
||||||
|
"version": "{{ .Version }}"
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
style="position: absolute; width: 0; height: 0; overflow: hidden;"
|
class="is-hidden"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
@@ -69,6 +70,5 @@
|
|||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script src="main.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import VueRouter from "vue-router";
|
import VueRouter from "vue-router";
|
||||||
import Meta from "vue-meta";
|
import Meta from "vue-meta";
|
||||||
import { Dropdown, Switch } from "buefy";
|
import Dropdown from "buefy/dist/esm/dropdown";
|
||||||
|
import Switch from "buefy/dist/esm/switch";
|
||||||
import store from "./store";
|
import store from "./store";
|
||||||
|
import config from "./store/config";
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
import Container from "./pages/Container.vue";
|
import Container from "./pages/Container.vue";
|
||||||
import Settings from "./pages/Settings.vue";
|
import Settings from "./pages/Settings.vue";
|
||||||
import Index from "./pages/Index.vue";
|
import Index from "./pages/Index.vue";
|
||||||
|
import Show from "./pages/Show.vue";
|
||||||
|
|
||||||
Vue.use(VueRouter);
|
Vue.use(VueRouter);
|
||||||
Vue.use(Meta);
|
Vue.use(Meta);
|
||||||
@@ -30,11 +33,16 @@ const routes = [
|
|||||||
component: Settings,
|
component: Settings,
|
||||||
name: "settings",
|
name: "settings",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/show",
|
||||||
|
component: Show,
|
||||||
|
name: "show",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const router = new VueRouter({
|
const router = new VueRouter({
|
||||||
mode: "history",
|
mode: "history",
|
||||||
base: BASE_PATH + "/",
|
base: config.base + "/",
|
||||||
routes,
|
routes,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Dozzle Log Viewer",
|
|
||||||
"short_name": "Dozzle",
|
|
||||||
"theme_color": "#111111",
|
|
||||||
"background_color": "#111111",
|
|
||||||
"display": "standalone",
|
|
||||||
"scope": "/",
|
|
||||||
"start_url": "/"
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<template lang="html">
|
<template>
|
||||||
<scrollable-view :scrollable="activeContainers.length > 0">
|
<scrollable-view :scrollable="activeContainers.length > 0">
|
||||||
<template v-slot:header v-if="activeContainers.length > 0">
|
<template v-slot:header v-if="activeContainers.length > 0">
|
||||||
<container-title :value="allContainersById[id].name"></container-title>
|
<container-title :value="allContainersById[id].name"></container-title>
|
||||||
|
|||||||
@@ -2,11 +2,6 @@ import { shallowMount } from "@vue/test-utils";
|
|||||||
import Index from "./Index";
|
import Index from "./Index";
|
||||||
|
|
||||||
describe("<Index />", () => {
|
describe("<Index />", () => {
|
||||||
test("is a Vue instance", () => {
|
|
||||||
const wrapper = shallowMount(Index);
|
|
||||||
expect(wrapper.isVueInstance()).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("renders correctly", () => {
|
test("renders correctly", () => {
|
||||||
const wrapper = shallowMount(Index);
|
const wrapper = shallowMount(Index);
|
||||||
expect(wrapper.element).toMatchSnapshot();
|
expect(wrapper.element).toMatchSnapshot();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template lang="html">
|
<template>
|
||||||
<div class="hero is-fullheight is-dark">
|
<div class="hero is-fullheight">
|
||||||
<div class="hero-body">
|
<div class="hero-body">
|
||||||
<div class="container has-text-centered">
|
<div class="container has-text-centered">
|
||||||
<h1 class="title">Please choose a container from the list to view the logs</h1>
|
<h1 class="title">Please choose a container from the list to view the logs</h1>
|
||||||
@@ -14,9 +14,3 @@ export default {
|
|||||||
name: "Default",
|
name: "Default",
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
|
||||||
.hero.is-dark {
|
|
||||||
color: #ddd;
|
|
||||||
background-color: #111;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template lang="html">
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="has-underline">
|
<div class="has-underline">
|
||||||
@@ -10,7 +10,9 @@
|
|||||||
>.
|
>.
|
||||||
<span v-if="hasUpdate">
|
<span v-if="hasUpdate">
|
||||||
New version is available! Update to
|
New version is available! Update to
|
||||||
<a :href="nextRelease.html_url" class="next-release">{{ nextRelease.name }}</a
|
<a :href="nextRelease.html_url" class="next-release" target="_blank" rel="noreferrer noopener">{{
|
||||||
|
nextRelease.name
|
||||||
|
}}</a
|
||||||
>.
|
>.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -31,6 +33,24 @@
|
|||||||
</b-switch>
|
</b-switch>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="item">
|
||||||
|
<b-switch v-model="showTimestamp">
|
||||||
|
Show timestamps
|
||||||
|
</b-switch>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item">
|
||||||
|
<b-switch v-model="showAllContainers">
|
||||||
|
Show stopped containers
|
||||||
|
</b-switch>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item">
|
||||||
|
<b-switch v-model="lightTheme">
|
||||||
|
Use light theme
|
||||||
|
</b-switch>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<h2 class="title is-6 is-marginless">Font size</h2>
|
<h2 class="title is-6 is-marginless">Font size</h2>
|
||||||
Modify the font size when viewing logs.
|
Modify the font size when viewing logs.
|
||||||
@@ -40,7 +60,12 @@
|
|||||||
<span class="is-capitalized">{{ size }}</span>
|
<span class="is-capitalized">{{ size }}</span>
|
||||||
<span class="icon"><icon name="chevron-down"></icon></span>
|
<span class="icon"><icon name="chevron-down"></icon></span>
|
||||||
</button>
|
</button>
|
||||||
<b-dropdown-item :value="value" aria-role="listitem" v-for="value in ['small', 'medium', 'large']">
|
<b-dropdown-item
|
||||||
|
:value="value"
|
||||||
|
aria-role="listitem"
|
||||||
|
v-for="value in ['small', 'medium', 'large']"
|
||||||
|
:key="value"
|
||||||
|
>
|
||||||
<div class="media">
|
<div class="media">
|
||||||
<span class="icon keep-size">
|
<span class="icon keep-size">
|
||||||
<icon name="check" v-if="value == size"></icon>
|
<icon name="check" v-if="value == size"></icon>
|
||||||
@@ -61,6 +86,7 @@ import gt from "semver/functions/gt";
|
|||||||
import valid from "semver/functions/valid";
|
import valid from "semver/functions/valid";
|
||||||
import { mapActions, mapState } from "vuex";
|
import { mapActions, mapState } from "vuex";
|
||||||
import Icon from "../components/Icon";
|
import Icon from "../components/Icon";
|
||||||
|
import config from "../store/config";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: [],
|
props: [],
|
||||||
@@ -70,7 +96,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
currentVersion: VERSION,
|
currentVersion: config.version,
|
||||||
nextRelease: null,
|
nextRelease: null,
|
||||||
hasUpdate: false,
|
hasUpdate: false,
|
||||||
};
|
};
|
||||||
@@ -92,23 +118,26 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["settings"]),
|
...mapState(["settings"]),
|
||||||
...["search", "size", "smallerScrollbars"].reduce((map, name) => {
|
...["search", "size", "smallerScrollbars", "showTimestamp", "showAllContainers", "lightTheme"].reduce(
|
||||||
map[name] = {
|
(map, name) => {
|
||||||
get() {
|
map[name] = {
|
||||||
return this.settings[name];
|
get() {
|
||||||
},
|
return this.settings[name];
|
||||||
set(value) {
|
},
|
||||||
this.updateSetting({ [name]: value });
|
set(value) {
|
||||||
},
|
this.updateSetting({ [name]: value });
|
||||||
};
|
},
|
||||||
return map;
|
};
|
||||||
}, {}),
|
return map;
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss" scoped>
|
||||||
.title {
|
.title {
|
||||||
color: #eee;
|
color: var(--title-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
a.next-release {
|
a.next-release {
|
||||||
@@ -125,7 +154,7 @@ a.next-release {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.has-underline {
|
.has-underline {
|
||||||
border-bottom: 1px solid #fff;
|
border-bottom: 1px solid var(--title-color);
|
||||||
padding: 1em 0px;
|
padding: 1em 0px;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|||||||
29
assets/pages/Show.vue
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<template> </template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapActions, mapGetters, mapState } from "vuex";
|
||||||
|
export default {
|
||||||
|
props: [],
|
||||||
|
name: "Show",
|
||||||
|
computed: mapGetters(["visibleContainers"]),
|
||||||
|
watch: {
|
||||||
|
visibleContainers(newValue) {
|
||||||
|
if (newValue) {
|
||||||
|
if (this.$route.query.name) {
|
||||||
|
const [container, _] = this.visibleContainers.filter((c) => c.name == this.$route.query.name);
|
||||||
|
if (container) {
|
||||||
|
this.$router.push({ name: "container", params: { id: container.id } });
|
||||||
|
} else {
|
||||||
|
console.error(`No containers found matching name=${this.$route.query.name}. Redirecting to /`);
|
||||||
|
this.$router.push({ name: "default" });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error(`Expection query parameter name to be set. Redirecting to /`);
|
||||||
|
this.$router.push({ name: "default" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
exports[`<Index /> renders correctly 1`] = `
|
exports[`<Index /> renders correctly 1`] = `
|
||||||
<div
|
<div
|
||||||
class="hero is-fullheight is-dark"
|
class="hero is-fullheight"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="hero-body"
|
class="hero-body"
|
||||||
|
|||||||
2
assets/store/config.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
const config = JSON.parse(document.querySelector("script#config__json").textContent);
|
||||||
|
export default config;
|
||||||
@@ -2,6 +2,7 @@ import Vue from "vue";
|
|||||||
import Vuex from "vuex";
|
import Vuex from "vuex";
|
||||||
import storage from "store/dist/store.modern";
|
import storage from "store/dist/store.modern";
|
||||||
import { DEFAULT_SETTINGS, DOZZLE_SETTINGS_KEY } from "./settings";
|
import { DEFAULT_SETTINGS, DOZZLE_SETTINGS_KEY } from "./settings";
|
||||||
|
import config from "./config";
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ const actions = {
|
|||||||
commit("SET_SEARCH", filter);
|
commit("SET_SEARCH", filter);
|
||||||
},
|
},
|
||||||
async FETCH_CONTAINERS({ commit }) {
|
async FETCH_CONTAINERS({ commit }) {
|
||||||
const containers = await (await fetch(`${BASE_PATH}/api/containers.json`)).json();
|
const containers = await (await fetch(`${config.base}/api/containers.json`)).json();
|
||||||
commit("SET_CONTAINERS", containers);
|
commit("SET_CONTAINERS", containers);
|
||||||
},
|
},
|
||||||
UPDATE_SETTING({ commit }, setting) {
|
UPDATE_SETTING({ commit }, setting) {
|
||||||
@@ -58,21 +59,25 @@ const actions = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
const getters = {
|
const getters = {
|
||||||
activeContainersById(state) {
|
activeContainersById({ activeContainers }) {
|
||||||
return state.activeContainers.reduce((map, obj) => {
|
return activeContainers.reduce((map, obj) => {
|
||||||
map[obj.id] = obj;
|
map[obj.id] = obj;
|
||||||
return map;
|
return map;
|
||||||
}, {});
|
}, {});
|
||||||
},
|
},
|
||||||
allContainersById(state) {
|
allContainersById({ containers }) {
|
||||||
return state.containers.reduce((map, obj) => {
|
return containers.reduce((map, obj) => {
|
||||||
map[obj.id] = obj;
|
map[obj.id] = obj;
|
||||||
return map;
|
return map;
|
||||||
}, {});
|
}, {});
|
||||||
},
|
},
|
||||||
|
visibleContainers({ containers, settings: { showAllContainers } }) {
|
||||||
|
const filter = showAllContainers ? () => true : (c) => c.state === "running";
|
||||||
|
return containers.filter(filter);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const es = new EventSource(`${BASE_PATH}/api/events/stream`);
|
const es = new EventSource(`${config.base}/api/events/stream`);
|
||||||
es.addEventListener("containers-changed", (e) => setTimeout(() => store.dispatch("FETCH_CONTAINERS"), 1000), false);
|
es.addEventListener("containers-changed", (e) => setTimeout(() => store.dispatch("FETCH_CONTAINERS"), 1000), false);
|
||||||
mql.addListener((e) => store.commit("SET_MOBILE_WIDTH", e.matches));
|
mql.addListener((e) => store.commit("SET_MOBILE_WIDTH", e.matches));
|
||||||
|
|
||||||
|
|||||||
@@ -4,4 +4,7 @@ export const DEFAULT_SETTINGS = {
|
|||||||
size: "medium",
|
size: "medium",
|
||||||
menuWidth: 15,
|
menuWidth: 15,
|
||||||
smallerScrollbars: false,
|
smallerScrollbars: false,
|
||||||
|
showTimestamp: true,
|
||||||
|
showAllContainers: false,
|
||||||
|
lightTheme: false
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,22 @@
|
|||||||
@charset "utf-8";
|
@charset "utf-8";
|
||||||
|
@import "~bulma/sass/utilities/initial-variables.sass";
|
||||||
|
|
||||||
$menu-item-active-background-color: hsl(171, 100%, 41%);
|
$body-family: "Roboto", sans-serif;
|
||||||
$menu-item-color: hsl(0, 6%, 87%);
|
$body-background-color: var(--body-background-color);
|
||||||
|
$body-color: var(--body-color);
|
||||||
|
|
||||||
|
$scheme-main: var(--scheme-main);
|
||||||
|
$scheme-main-bis: var(--scheme-main-bis);
|
||||||
|
$scheme-main-ter: var(--scheme-main-ter);
|
||||||
|
|
||||||
|
$border: var(--border-color);
|
||||||
|
$border-hover: var(--border-hover-color);
|
||||||
|
|
||||||
|
$menu-item-active-background-color: var(--menu-item-active-background-color);
|
||||||
|
$menu-item-color: var(--menu-item-color);
|
||||||
|
$menu-item-hover-background-color: var(--menu-item-hover-background-color);
|
||||||
|
|
||||||
|
$title-color: var(--title-color);
|
||||||
|
|
||||||
@import "~bulma";
|
@import "~bulma";
|
||||||
@import "../node_modules/splitpanes/dist/splitpanes.css";
|
@import "../node_modules/splitpanes/dist/splitpanes.css";
|
||||||
@@ -9,19 +24,51 @@ $menu-item-color: hsl(0, 6%, 87%);
|
|||||||
@import "~buefy/src/scss/components/_dropdown";
|
@import "~buefy/src/scss/components/_dropdown";
|
||||||
@import "~buefy/src/scss/components/_switch";
|
@import "~buefy/src/scss/components/_switch";
|
||||||
|
|
||||||
body {
|
html {
|
||||||
font-family: "Roboto", sans-serif;
|
--scheme-main: #{$black};
|
||||||
color: #ddd;
|
--scheme-main-bis: #{$black-bis};
|
||||||
background-color: #111;
|
--scheme-main-ter: #{$black-ter};
|
||||||
|
|
||||||
|
--border-color: #{$grey-darker};
|
||||||
|
--border-hover-color: var(--secondary-color);
|
||||||
|
|
||||||
|
--primary-color: #{$turquoise};
|
||||||
|
--secondary-color: #{$yellow};
|
||||||
|
|
||||||
|
--body-background-color: #{$black-bis};
|
||||||
|
--body-color: #{$grey-lighter};
|
||||||
|
|
||||||
|
--menu-item-active-background-color: var(--primary-color);
|
||||||
|
--menu-item-color: hsl(0, 6%, 87%);
|
||||||
|
--menu-item-hover-background-color: #{$white-ter};
|
||||||
|
|
||||||
|
--title-color: #{$grey-lightest};
|
||||||
}
|
}
|
||||||
|
|
||||||
h1.title {
|
[data-theme="light"] {
|
||||||
font-family: "Gafata", sans-serif;
|
--scheme-main: #{$white};
|
||||||
|
--scheme-main-bis: #{$white-bis};
|
||||||
|
--scheme-main-ter: #{$white-ter};
|
||||||
|
|
||||||
|
--border-color: #{$grey-lighter};
|
||||||
|
--border-hover-color: var(--secondary-color);
|
||||||
|
|
||||||
|
--primary-color: #{$turquoise};
|
||||||
|
--secondary-color: #d8f0ca;
|
||||||
|
|
||||||
|
--body-background-color: #{$white-bis};
|
||||||
|
--body-color: #{$grey-darker};
|
||||||
|
|
||||||
|
--menu-item-color: #{$grey-dark};
|
||||||
|
--menu-item-hover-background-color: #eee8e7;
|
||||||
|
|
||||||
|
--title-color: #{$grey-dark};
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
overflow-x: unset;
|
overflow-x: unset;
|
||||||
overflow-y: unset;
|
overflow-y: unset;
|
||||||
|
scroll-snap-type: y proximity;
|
||||||
}
|
}
|
||||||
|
|
||||||
html.has-custom-scrollbars {
|
html.has-custom-scrollbars {
|
||||||
@@ -55,11 +102,9 @@ html.has-custom-scrollbars {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.is-settings-control {
|
.is-settings-control {
|
||||||
|
|
||||||
background: rgba(0, 0, 0, 0.4);
|
background: rgba(0, 0, 0, 0.4);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: rgb(255, 221, 87) !important;
|
border-color: rgb(255, 221, 87) !important;
|
||||||
background: rgba(0, 0, 0, 0.8) !important;
|
background: rgba(0, 0, 0, 0.8) !important;
|
||||||
@@ -72,4 +117,3 @@ html.has-custom-scrollbars {
|
|||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ type dockerProxy interface {
|
|||||||
|
|
||||||
// Client is a proxy around the docker client
|
// Client is a proxy around the docker client
|
||||||
type Client interface {
|
type Client interface {
|
||||||
ListContainers(showAll bool) ([]Container, error)
|
ListContainers() ([]Container, error)
|
||||||
FindContainer(string) (Container, error)
|
FindContainer(string) (Container, error)
|
||||||
ContainerLogs(context.Context, string, int) (<-chan string, <-chan error)
|
ContainerLogs(context.Context, string, int, string) (<-chan string, <-chan error)
|
||||||
Events(context.Context) (<-chan events.Message, <-chan error)
|
Events(context.Context) (<-chan events.Message, <-chan error)
|
||||||
ContainerLogsBetweenDates(context.Context, string, time.Time, time.Time) ([]string, error)
|
ContainerLogsBetweenDates(context.Context, string, time.Time, time.Time) ([]string, error)
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ func NewClientWithFilters(f map[string]string) Client {
|
|||||||
|
|
||||||
log.Debugf("filterArgs = %v", filterArgs)
|
log.Debugf("filterArgs = %v", filterArgs)
|
||||||
|
|
||||||
cli, err := client.NewClientWithOpts(client.FromEnv)
|
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@@ -65,7 +65,7 @@ func NewClientWithFilters(f map[string]string) Client {
|
|||||||
|
|
||||||
func (d *dockerClient) FindContainer(id string) (Container, error) {
|
func (d *dockerClient) FindContainer(id string) (Container, error) {
|
||||||
var container Container
|
var container Container
|
||||||
containers, err := d.ListContainers(true)
|
containers, err := d.ListContainers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return container, err
|
return container, err
|
||||||
}
|
}
|
||||||
@@ -85,10 +85,10 @@ func (d *dockerClient) FindContainer(id string) (Container, error) {
|
|||||||
return container, nil
|
return container, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dockerClient) ListContainers(showAll bool) ([]Container, error) {
|
func (d *dockerClient) ListContainers() ([]Container, error) {
|
||||||
containerListOptions := types.ContainerListOptions{
|
containerListOptions := types.ContainerListOptions{
|
||||||
Filters: d.filters,
|
Filters: d.filters,
|
||||||
All: showAll,
|
All: true,
|
||||||
}
|
}
|
||||||
list, err := d.cli.ContainerList(context.Background(), containerListOptions)
|
list, err := d.cli.ContainerList(context.Background(), containerListOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -113,7 +113,7 @@ func (d *dockerClient) ListContainers(showAll bool) ([]Container, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(containers, func(i, j int) bool {
|
sort.Slice(containers, func(i, j int) bool {
|
||||||
return containers[i].Name < containers[j].Name
|
return strings.ToLower(containers[i].Name) < strings.ToLower(containers[j].Name)
|
||||||
})
|
})
|
||||||
|
|
||||||
if containers == nil {
|
if containers == nil {
|
||||||
@@ -151,8 +151,17 @@ func logReader(reader io.ReadCloser, tty bool) func() (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dockerClient) ContainerLogs(ctx context.Context, id string, tailSize int) (<-chan string, <-chan error) {
|
func (d *dockerClient) ContainerLogs(ctx context.Context, id string, tailSize int, since string) (<-chan string, <-chan error) {
|
||||||
options := types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: strconv.Itoa(tailSize), Timestamps: true}
|
log.WithField("id", id).WithField("since", since).Debug("Streaming logs for container")
|
||||||
|
|
||||||
|
options := types.ContainerLogsOptions{
|
||||||
|
ShowStdout: true,
|
||||||
|
ShowStderr: true,
|
||||||
|
Follow: true,
|
||||||
|
Tail: strconv.Itoa(tailSize),
|
||||||
|
Timestamps: true,
|
||||||
|
Since: since,
|
||||||
|
}
|
||||||
reader, err := d.cli.ContainerLogs(ctx, id, options)
|
reader, err := d.cli.ContainerLogs(ctx, id, options)
|
||||||
errChannel := make(chan error, 1)
|
errChannel := make(chan error, 1)
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ func Test_dockerClient_ListContainers_null(t *testing.T) {
|
|||||||
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(nil, nil)
|
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(nil, nil)
|
||||||
client := &dockerClient{proxy, filters.NewArgs()}
|
client := &dockerClient{proxy, filters.NewArgs()}
|
||||||
|
|
||||||
list, err := client.ListContainers(true)
|
list, err := client.ListContainers()
|
||||||
assert.Empty(t, list, "list should be empty")
|
assert.Empty(t, list, "list should be empty")
|
||||||
require.NoError(t, err, "error should not return an error.")
|
require.NoError(t, err, "error should not return an error.")
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ func Test_dockerClient_ListContainers_error(t *testing.T) {
|
|||||||
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(nil, errors.New("test"))
|
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(nil, errors.New("test"))
|
||||||
client := &dockerClient{proxy, filters.NewArgs()}
|
client := &dockerClient{proxy, filters.NewArgs()}
|
||||||
|
|
||||||
list, err := client.ListContainers(true)
|
list, err := client.ListContainers()
|
||||||
assert.Nil(t, list, "list should be nil")
|
assert.Nil(t, list, "list should be nil")
|
||||||
require.Error(t, err, "test.")
|
require.Error(t, err, "test.")
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ func Test_dockerClient_ListContainers_happy(t *testing.T) {
|
|||||||
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(containers, nil)
|
proxy.On("ContainerList", mock.Anything, mock.Anything).Return(containers, nil)
|
||||||
client := &dockerClient{proxy, filters.NewArgs()}
|
client := &dockerClient{proxy, filters.NewArgs()}
|
||||||
|
|
||||||
list, err := client.ListContainers(true)
|
list, err := client.ListContainers()
|
||||||
require.NoError(t, err, "error should not return an error.")
|
require.NoError(t, err, "error should not return an error.")
|
||||||
|
|
||||||
assert.Equal(t, list, []Container{
|
assert.Equal(t, list, []Container{
|
||||||
@@ -120,14 +120,14 @@ func Test_dockerClient_ContainerLogs_happy(t *testing.T) {
|
|||||||
b = append(b, []byte(expected)...)
|
b = append(b, []byte(expected)...)
|
||||||
|
|
||||||
reader := ioutil.NopCloser(bytes.NewReader(b))
|
reader := ioutil.NopCloser(bytes.NewReader(b))
|
||||||
options := types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: "300", Timestamps: true}
|
options := types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: "300", Timestamps: true, Since: "since"}
|
||||||
proxy.On("ContainerLogs", mock.Anything, id, options).Return(reader, nil)
|
proxy.On("ContainerLogs", mock.Anything, id, options).Return(reader, nil)
|
||||||
|
|
||||||
json := types.ContainerJSON{Config: &container.Config{Tty: false}}
|
json := types.ContainerJSON{Config: &container.Config{Tty: false}}
|
||||||
proxy.On("ContainerInspect", mock.Anything, id).Return(json, nil)
|
proxy.On("ContainerInspect", mock.Anything, id).Return(json, nil)
|
||||||
|
|
||||||
client := &dockerClient{proxy, filters.NewArgs()}
|
client := &dockerClient{proxy, filters.NewArgs()}
|
||||||
messages, _ := client.ContainerLogs(context.Background(), id, 300)
|
messages, _ := client.ContainerLogs(context.Background(), id, 300, "since")
|
||||||
|
|
||||||
actual, _ := <-messages
|
actual, _ := <-messages
|
||||||
assert.Equal(t, expected, actual, "message doesn't match expected")
|
assert.Equal(t, expected, actual, "message doesn't match expected")
|
||||||
@@ -151,7 +151,7 @@ func Test_dockerClient_ContainerLogs_happy_with_tty(t *testing.T) {
|
|||||||
proxy.On("ContainerInspect", mock.Anything, id).Return(json, nil)
|
proxy.On("ContainerInspect", mock.Anything, id).Return(json, nil)
|
||||||
|
|
||||||
client := &dockerClient{proxy, filters.NewArgs()}
|
client := &dockerClient{proxy, filters.NewArgs()}
|
||||||
messages, _ := client.ContainerLogs(context.Background(), id, 300)
|
messages, _ := client.ContainerLogs(context.Background(), id, 300, "")
|
||||||
|
|
||||||
actual, _ := <-messages
|
actual, _ := <-messages
|
||||||
assert.Equal(t, expected, actual, "message doesn't match expected")
|
assert.Equal(t, expected, actual, "message doesn't match expected")
|
||||||
@@ -169,7 +169,7 @@ func Test_dockerClient_ContainerLogs_error(t *testing.T) {
|
|||||||
|
|
||||||
client := &dockerClient{proxy, filters.NewArgs()}
|
client := &dockerClient{proxy, filters.NewArgs()}
|
||||||
|
|
||||||
messages, err := client.ContainerLogs(context.Background(), id, 300)
|
messages, err := client.ContainerLogs(context.Background(), id, 300, "")
|
||||||
|
|
||||||
assert.Nil(t, messages, "messages should be nil")
|
assert.Nil(t, messages, "messages should be nil")
|
||||||
|
|
||||||
|
|||||||
7
go.mod
@@ -34,17 +34,16 @@ require (
|
|||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.5.2 // indirect
|
github.com/rogpeppe/go-internal v1.5.2 // indirect
|
||||||
github.com/sergi/go-diff v1.0.0 // indirect
|
github.com/sergi/go-diff v1.0.0 // indirect
|
||||||
github.com/sirupsen/logrus v1.5.0
|
github.com/sirupsen/logrus v1.6.0
|
||||||
github.com/spf13/afero v1.2.2 // indirect
|
github.com/spf13/afero v1.2.2 // indirect
|
||||||
github.com/spf13/cast v1.3.1 // indirect
|
github.com/spf13/cast v1.3.1 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/spf13/viper v1.6.2
|
github.com/spf13/viper v1.7.0
|
||||||
github.com/stretchr/objx v0.2.0 // indirect
|
github.com/stretchr/objx v0.2.0 // indirect
|
||||||
github.com/stretchr/testify v1.5.1
|
github.com/stretchr/testify v1.6.1
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect
|
||||||
golang.org/x/text v0.3.2 // indirect
|
|
||||||
google.golang.org/genproto v0.0.0-20200226201735-46b91f19d98c // indirect
|
google.golang.org/genproto v0.0.0-20200226201735-46b91f19d98c // indirect
|
||||||
google.golang.org/grpc v1.27.1 // indirect
|
google.golang.org/grpc v1.27.1 // indirect
|
||||||
gopkg.in/ini.v1 v1.52.0 // indirect
|
gopkg.in/ini.v1 v1.52.0 // indirect
|
||||||
|
|||||||
156
go.sum
@@ -1,25 +1,42 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
|
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||||
|
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||||
|
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||||
|
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||||
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
|
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||||
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||||
github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
||||||
github.com/beme/abide v0.0.0-20190723115211-635a09831760 h1:FvTM5NSN5HYvfKpgL+8x73U5v063vHsd7AX05eV1DnM=
|
github.com/beme/abide v0.0.0-20190723115211-635a09831760 h1:FvTM5NSN5HYvfKpgL+8x73U5v063vHsd7AX05eV1DnM=
|
||||||
github.com/beme/abide v0.0.0-20190723115211-635a09831760/go.mod h1:6+8gCKsZnxzhGTmKRh4BSkLos9CbWRJNcrp55We4SqQ=
|
github.com/beme/abide v0.0.0-20190723115211-635a09831760/go.mod h1:6+8gCKsZnxzhGTmKRh4BSkLos9CbWRJNcrp55We4SqQ=
|
||||||
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||||
|
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||||
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
|
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
|
||||||
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
||||||
@@ -32,8 +49,10 @@ github.com/containerd/containerd v1.3.3 h1:LoIzb5y9x5l8VKAlyrbusNPXqBY0+kviRloxF
|
|||||||
github.com/containerd/containerd v1.3.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
github.com/containerd/containerd v1.3.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
|
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
|
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||||
@@ -57,10 +76,12 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
|
|||||||
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
@@ -87,6 +108,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekf
|
|||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||||
@@ -96,10 +119,19 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
|||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
|
github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
|
||||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||||
@@ -107,17 +139,40 @@ github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z
|
|||||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
|
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||||
|
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
|
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
|
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
@@ -128,6 +183,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
|||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
@@ -138,11 +195,24 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
|
|||||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
||||||
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
|
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
|
||||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
@@ -154,6 +224,7 @@ github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQ
|
|||||||
github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||||
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=
|
github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=
|
||||||
@@ -166,6 +237,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||||
@@ -189,7 +261,9 @@ github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
|
|||||||
github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w=
|
github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w=
|
||||||
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
@@ -198,6 +272,8 @@ github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4
|
|||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q=
|
github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q=
|
||||||
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
|
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
|
||||||
|
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
||||||
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
@@ -225,8 +301,10 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
|||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
|
github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
|
||||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||||
github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E=
|
github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs=
|
||||||
github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
|
github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw=
|
||||||
|
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
|
||||||
|
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||||
@@ -238,10 +316,13 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
|
|||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho=
|
||||||
|
github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
|
||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
@@ -249,56 +330,91 @@ github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX
|
|||||||
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
|
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
|
||||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
|
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
|
||||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||||
|
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||||
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
|
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
|
||||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438 h1:khxRGsvPk4n2y8I/mLLjp7e5dMTJmH75wvqS6nMwUtY=
|
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438 h1:khxRGsvPk4n2y8I/mLLjp7e5dMTJmH75wvqS6nMwUtY=
|
||||||
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@@ -307,22 +423,50 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm
|
|||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||||
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
|
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||||
|
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
google.golang.org/genproto v0.0.0-20200226201735-46b91f19d98c h1:xFOdgVPpeowWAH0MJ5i0XMp+3yWiWamMtN/kx9xThIQ=
|
google.golang.org/genproto v0.0.0-20200226201735-46b91f19d98c h1:xFOdgVPpeowWAH0MJ5i0XMp+3yWiWamMtN/kx9xThIQ=
|
||||||
google.golang.org/genproto v0.0.0-20200226201735-46b91f19d98c/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200226201735-46b91f19d98c/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
|
google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
|
||||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
|
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
|
||||||
@@ -346,7 +490,13 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
|||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
|||||||
1
integration/.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
node_modules
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
FROM amir20/docker-alpine-puppeteer:edge
|
FROM amir20/docker-alpine-puppeteer:edge
|
||||||
|
|
||||||
COPY --chown=pptruser:pptruser package*.json /app/
|
COPY --chown=pptruser:pptruser package*.json yarn.lock /app/
|
||||||
RUN yarn
|
RUN yarn
|
||||||
|
|
||||||
COPY --chown=pptruser:pptruser . /app/
|
COPY --chown=pptruser:pptruser . /app/
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 61 KiB |
@@ -7,11 +7,11 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- DOZZLE_FILTER=name=dozzle
|
- DOZZLE_FILTER=name=dozzle
|
||||||
build:
|
build:
|
||||||
context: ..
|
context: ..
|
||||||
integration:
|
integration:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
command: npm test
|
command: yarn test
|
||||||
environment:
|
environment:
|
||||||
- BASE=http://dozzle:8080/
|
- BASE=http://dozzle:8080/
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"jest": "^25.2.6",
|
"jest": "^26.0.1",
|
||||||
"jest-image-snapshot": "^3.0.1",
|
"jest-image-snapshot": "^4.0.0",
|
||||||
"puppeteer": "^2.1.1"
|
"puppeteer": "^4.0.0"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"preset": "jest-puppeteer",
|
"preset": "jest-puppeteer",
|
||||||
|
|||||||
@@ -29,10 +29,17 @@ describe("home page", () => {
|
|||||||
expect(image).toMatchImageSnapshot();
|
expect(image).toMatchImageSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("displays iphone menu", async () => {
|
||||||
|
await page.emulate(iPhoneX);
|
||||||
|
await page.click("a.navbar-burger");
|
||||||
|
|
||||||
|
const menuText = await page.$eval("aside ul.menu-list.is-hidden-mobile li a", (e) => e.textContent);
|
||||||
|
expect(menuText.trim()).toEqual("dozzle");
|
||||||
|
});
|
||||||
|
|
||||||
describe("has menu visible", () => {
|
describe("has menu visible", () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await jestPuppeteer.resetBrowser();
|
await jestPuppeteer.resetBrowser();
|
||||||
// await page.setViewport({ width: 1920, height: 1200 });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
|||||||
13
jest.config.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
module.exports = {
|
||||||
|
clearMocks: true,
|
||||||
|
moduleFileExtensions: ["js", "json", "vue"],
|
||||||
|
coveragePathIgnorePatterns: ["node_modules"],
|
||||||
|
testPathIgnorePatterns: ["node_modules", "<rootDir>/integration/"],
|
||||||
|
transformIgnorePatterns: ["node_modules"],
|
||||||
|
watchPathIgnorePatterns: ["<rootDir>/node_modules/"],
|
||||||
|
snapshotSerializers: ["jest-serializer-vue"],
|
||||||
|
transform: {
|
||||||
|
".*\\.vue$": "vue-jest",
|
||||||
|
"^.+\\.js$": "babel-jest",
|
||||||
|
},
|
||||||
|
};
|
||||||
194
main.go
@@ -2,20 +2,15 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"html/template"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amir20/dozzle/docker"
|
"github.com/amir20/dozzle/docker"
|
||||||
"github.com/gobuffalo/packr"
|
"github.com/gobuffalo/packr"
|
||||||
"github.com/gorilla/mux"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
@@ -32,15 +27,13 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type handler struct {
|
type handler struct {
|
||||||
client docker.Client
|
client docker.Client
|
||||||
showAll bool
|
box packr.Box
|
||||||
box packr.Box
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
pflag.String("addr", ":8080", "http service address")
|
pflag.String("addr", ":8080", "http service address")
|
||||||
pflag.String("base", "/", "base address of the application to mount")
|
pflag.String("base", "/", "base address of the application to mount")
|
||||||
pflag.Bool("showAll", false, "show all containers, even stopped")
|
|
||||||
pflag.String("level", "info", "logging level")
|
pflag.String("level", "info", "logging level")
|
||||||
pflag.Int("tailSize", 300, "Tail size to use for initial container logs")
|
pflag.Int("tailSize", 300, "Tail size to use for initial container logs")
|
||||||
pflag.StringToStringVar(&filters, "filter", map[string]string{}, "Container filters to use for showing logs")
|
pflag.StringToStringVar(&filters, "filter", map[string]string{}, "Container filters to use for showing logs")
|
||||||
@@ -54,10 +47,9 @@ func init() {
|
|||||||
base = viper.GetString("base")
|
base = viper.GetString("base")
|
||||||
level = viper.GetString("level")
|
level = viper.GetString("level")
|
||||||
tailSize = viper.GetInt("tailSize")
|
tailSize = viper.GetInt("tailSize")
|
||||||
showAll = viper.GetBool("showAll")
|
|
||||||
|
|
||||||
// Until https://github.com/spf13/viper/issues/608 is fixed. We have to use this hacky way.
|
// Until https://github.com/spf13/viper/issues/911 is fixed. We have to use this hacky way.
|
||||||
// filters = viper.GetStringSlice("filter")
|
// filters = viper.GetStringMapString("filter")
|
||||||
if value, ok := os.LookupEnv("DOZZLE_FILTER"); ok {
|
if value, ok := os.LookupEnv("DOZZLE_FILTER"); ok {
|
||||||
log.Infof("Parsing %s", value)
|
log.Infof("Parsing %s", value)
|
||||||
urlValues, err := url.ParseQuery(strings.ReplaceAll(value, ",", "&"))
|
urlValues, err := url.ParseQuery(strings.ReplaceAll(value, ",", "&"))
|
||||||
@@ -79,27 +71,10 @@ func init() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func createRoutes(base string, h *handler) *mux.Router {
|
|
||||||
r := mux.NewRouter()
|
|
||||||
if base != "/" {
|
|
||||||
r.HandleFunc(base, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
||||||
http.Redirect(w, req, base+"/", http.StatusMovedPermanently)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
s := r.PathPrefix(base).Subrouter()
|
|
||||||
s.HandleFunc("/api/containers.json", h.listContainers)
|
|
||||||
s.HandleFunc("/api/logs/stream", h.streamLogs)
|
|
||||||
s.HandleFunc("/api/logs", h.fetchLogsBetweenDates)
|
|
||||||
s.HandleFunc("/api/events/stream", h.streamEvents)
|
|
||||||
s.HandleFunc("/version", h.version)
|
|
||||||
s.PathPrefix("/").Handler(http.StripPrefix(base, http.HandlerFunc(h.index)))
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.Infof("Dozzle version %s", version)
|
log.Infof("Dozzle version %s", version)
|
||||||
dockerClient := docker.NewClientWithFilters(filters)
|
dockerClient := docker.NewClientWithFilters(filters)
|
||||||
_, err := dockerClient.ListContainers(true)
|
_, err := dockerClient.ListContainers()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Could not connect to Docker Engine: %v", err)
|
log.Fatalf("Could not connect to Docker Engine: %v", err)
|
||||||
@@ -107,9 +82,8 @@ func main() {
|
|||||||
|
|
||||||
box := packr.NewBox("./static")
|
box := packr.NewBox("./static")
|
||||||
r := createRoutes(base, &handler{
|
r := createRoutes(base, &handler{
|
||||||
client: dockerClient,
|
client: dockerClient,
|
||||||
showAll: showAll,
|
box: box,
|
||||||
box: box,
|
|
||||||
})
|
})
|
||||||
srv := &http.Server{Addr: addr, Handler: r}
|
srv := &http.Server{Addr: addr, Handler: r}
|
||||||
|
|
||||||
@@ -130,157 +104,3 @@ func main() {
|
|||||||
srv.Shutdown(ctx)
|
srv.Shutdown(ctx)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) index(w http.ResponseWriter, req *http.Request) {
|
|
||||||
fileServer := http.FileServer(h.box)
|
|
||||||
if h.box.Has(req.URL.Path) && req.URL.Path != "" && req.URL.Path != "/" {
|
|
||||||
fileServer.ServeHTTP(w, req)
|
|
||||||
} else {
|
|
||||||
text, err := h.box.FindString("index.html")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
text = strings.Replace(text, "__BASE__", "{{ .Base }}", -1)
|
|
||||||
tmpl, err := template.New("index.html").Parse(text)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
path := ""
|
|
||||||
if base != "/" {
|
|
||||||
path = base
|
|
||||||
}
|
|
||||||
|
|
||||||
data := struct {
|
|
||||||
Base string
|
|
||||||
Version string
|
|
||||||
}{path, version}
|
|
||||||
err = tmpl.Execute(w, data)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handler) listContainers(w http.ResponseWriter, r *http.Request) {
|
|
||||||
containers, err := h.client.ListContainers(h.showAll)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = json.NewEncoder(w).Encode(containers)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
|
|
||||||
|
|
||||||
from, _ := time.Parse(time.RFC3339, r.URL.Query().Get("from"))
|
|
||||||
to, _ := time.Parse(time.RFC3339, r.URL.Query().Get("to"))
|
|
||||||
id := r.URL.Query().Get("id")
|
|
||||||
|
|
||||||
messages, _ := h.client.ContainerLogsBetweenDates(r.Context(), id, from, to)
|
|
||||||
|
|
||||||
for _, m := range messages {
|
|
||||||
fmt.Fprintln(w, m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handler) streamLogs(w http.ResponseWriter, r *http.Request) {
|
|
||||||
id := r.URL.Query().Get("id")
|
|
||||||
if id == "" {
|
|
||||||
http.Error(w, "id is required", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
f, ok := w.(http.Flusher)
|
|
||||||
if !ok {
|
|
||||||
http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
container, e := h.client.FindContainer(id)
|
|
||||||
if e != nil {
|
|
||||||
http.Error(w, e.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
messages, err := h.client.ContainerLogs(r.Context(), container.ID, tailSize)
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/event-stream")
|
|
||||||
w.Header().Set("Cache-Control", "no-cache")
|
|
||||||
w.Header().Set("Connection", "keep-alive")
|
|
||||||
w.Header().Set("Transfer-Encoding", "chunked")
|
|
||||||
|
|
||||||
log.Debugf("Starting to stream logs for %s", id)
|
|
||||||
Loop:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case message, ok := <-messages:
|
|
||||||
if !ok {
|
|
||||||
break Loop
|
|
||||||
}
|
|
||||||
_, e := fmt.Fprintf(w, "data: %s\n\n", message)
|
|
||||||
if e != nil {
|
|
||||||
log.Debugf("Error while writing to log stream: %v", e)
|
|
||||||
break Loop
|
|
||||||
}
|
|
||||||
f.Flush()
|
|
||||||
case e := <-err:
|
|
||||||
log.Debugf("Error while reading from log stream: %v", e)
|
|
||||||
break Loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.WithField("NumGoroutine", runtime.NumGoroutine()).Debug("runtime stats")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
|
|
||||||
f, ok := w.(http.Flusher)
|
|
||||||
if !ok {
|
|
||||||
http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/event-stream")
|
|
||||||
w.Header().Set("Cache-Control", "no-cache")
|
|
||||||
w.Header().Set("Connection", "keep-alive")
|
|
||||||
w.Header().Set("Transfer-Encoding", "chunked")
|
|
||||||
|
|
||||||
ctx := r.Context()
|
|
||||||
messages, err := h.client.Events(ctx)
|
|
||||||
|
|
||||||
Loop:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case message, ok := <-messages:
|
|
||||||
if !ok {
|
|
||||||
break Loop
|
|
||||||
}
|
|
||||||
switch message.Action {
|
|
||||||
case "connect", "disconnect", "create", "destroy", "start", "stop":
|
|
||||||
log.Debugf("Triggering docker event: %v", message.Action)
|
|
||||||
_, err := fmt.Fprintf(w, "event: containers-changed\ndata: %s\n\n", message.Action)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Debugf("Error while writing to event stream: %v", err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
f.Flush()
|
|
||||||
default:
|
|
||||||
log.Debugf("Ignoring docker event: %v", message.Action)
|
|
||||||
}
|
|
||||||
case <-ctx.Done():
|
|
||||||
break Loop
|
|
||||||
case <-err:
|
|
||||||
break Loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handler) version(w http.ResponseWriter, r *http.Request) {
|
|
||||||
fmt.Fprintln(w, version)
|
|
||||||
}
|
|
||||||
|
|||||||
70
main_test.go
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
@@ -32,7 +33,7 @@ func (m *MockedClient) FindContainer(id string) (docker.Container, error) {
|
|||||||
return container, args.Error(1)
|
return container, args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) ListContainers(showAll bool) ([]docker.Container, error) {
|
func (m *MockedClient) ListContainers() ([]docker.Container, error) {
|
||||||
args := m.Called()
|
args := m.Called()
|
||||||
containers, ok := args.Get(0).([]docker.Container)
|
containers, ok := args.Get(0).([]docker.Container)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -41,7 +42,7 @@ func (m *MockedClient) ListContainers(showAll bool) ([]docker.Container, error)
|
|||||||
return containers, args.Error(1)
|
return containers, args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockedClient) ContainerLogs(ctx context.Context, id string, tailSize int) (<-chan string, <-chan error) {
|
func (m *MockedClient) ContainerLogs(ctx context.Context, id string, tailSize int, since string) (<-chan string, <-chan error) {
|
||||||
args := m.Called(ctx, id, tailSize)
|
args := m.Called(ctx, id, tailSize)
|
||||||
channel, ok := args.Get(0).(chan string)
|
channel, ok := args.Get(0).(chan string)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -124,6 +125,60 @@ func Test_handler_streamLogs_happy(t *testing.T) {
|
|||||||
mockedClient.AssertExpectations(t)
|
mockedClient.AssertExpectations(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_handler_streamLogs_happy_with_id(t *testing.T) {
|
||||||
|
id := "123456"
|
||||||
|
req, err := http.NewRequest("GET", "/api/logs/stream", nil)
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Add("id", id)
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
require.NoError(t, err, "NewRequest should not return an error.")
|
||||||
|
|
||||||
|
mockedClient := new(MockedClient)
|
||||||
|
|
||||||
|
messages := make(chan string)
|
||||||
|
errChannel := make(chan error)
|
||||||
|
mockedClient.On("FindContainer", id).Return(docker.Container{ID: id}, nil)
|
||||||
|
mockedClient.On("ContainerLogs", mock.Anything, mock.Anything, 300).Return(messages, errChannel)
|
||||||
|
go func() {
|
||||||
|
messages <- "2020-05-13T18:55:37.772853839Z INFO Testing logs..."
|
||||||
|
close(messages)
|
||||||
|
}()
|
||||||
|
|
||||||
|
h := handler{client: mockedClient}
|
||||||
|
handler := http.HandlerFunc(h.streamLogs)
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(rr, req)
|
||||||
|
abide.AssertHTTPResponse(t, t.Name(), rr.Result())
|
||||||
|
mockedClient.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_handler_streamLogs_happy_container_stopped(t *testing.T) {
|
||||||
|
id := "123456"
|
||||||
|
req, err := http.NewRequest("GET", "/api/logs/stream", nil)
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Add("id", id)
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
require.NoError(t, err, "NewRequest should not return an error.")
|
||||||
|
|
||||||
|
mockedClient := new(MockedClient)
|
||||||
|
messages := make(chan string)
|
||||||
|
errChannel := make(chan error)
|
||||||
|
mockedClient.On("FindContainer", id).Return(docker.Container{ID: id}, nil)
|
||||||
|
mockedClient.On("ContainerLogs", mock.Anything, id, 300).Return(messages, errChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
errChannel <- io.EOF
|
||||||
|
close(messages)
|
||||||
|
}()
|
||||||
|
|
||||||
|
h := handler{client: mockedClient}
|
||||||
|
handler := http.HandlerFunc(h.streamLogs)
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(rr, req)
|
||||||
|
abide.AssertHTTPResponse(t, t.Name(), rr.Result())
|
||||||
|
mockedClient.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
func Test_handler_streamLogs_error_finding_container(t *testing.T) {
|
func Test_handler_streamLogs_error_finding_container(t *testing.T) {
|
||||||
id := "123456"
|
id := "123456"
|
||||||
req, err := http.NewRequest("GET", "/api/logs/stream", nil)
|
req, err := http.NewRequest("GET", "/api/logs/stream", nil)
|
||||||
@@ -159,6 +214,7 @@ func Test_handler_streamLogs_error_reading(t *testing.T) {
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
errChannel <- errors.New("test error")
|
errChannel <- errors.New("test error")
|
||||||
|
close(messages)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
h := handler{client: mockedClient}
|
h := handler{client: mockedClient}
|
||||||
@@ -246,7 +302,7 @@ func Test_createRoutes_index(t *testing.T) {
|
|||||||
box := packr.NewBox("./virtual")
|
box := packr.NewBox("./virtual")
|
||||||
require.NoError(t, box.AddString("index.html", "index page"), "AddString should have no error.")
|
require.NoError(t, box.AddString("index.html", "index page"), "AddString should have no error.")
|
||||||
|
|
||||||
handler := createRoutes("/", &handler{mockedClient, true, box})
|
handler := createRoutes("/", &handler{mockedClient, box})
|
||||||
req, err := http.NewRequest("GET", "/", nil)
|
req, err := http.NewRequest("GET", "/", nil)
|
||||||
require.NoError(t, err, "NewRequest should not return an error.")
|
require.NoError(t, err, "NewRequest should not return an error.")
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@@ -259,7 +315,7 @@ func Test_createRoutes_redirect(t *testing.T) {
|
|||||||
mockedClient := new(MockedClient)
|
mockedClient := new(MockedClient)
|
||||||
box := packr.NewBox("./virtual")
|
box := packr.NewBox("./virtual")
|
||||||
|
|
||||||
handler := createRoutes("/foobar", &handler{mockedClient, true, box})
|
handler := createRoutes("/foobar", &handler{mockedClient, box})
|
||||||
req, err := http.NewRequest("GET", "/foobar", nil)
|
req, err := http.NewRequest("GET", "/foobar", nil)
|
||||||
require.NoError(t, err, "NewRequest should not return an error.")
|
require.NoError(t, err, "NewRequest should not return an error.")
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@@ -273,7 +329,7 @@ func Test_createRoutes_foobar(t *testing.T) {
|
|||||||
box := packr.NewBox("./virtual")
|
box := packr.NewBox("./virtual")
|
||||||
require.NoError(t, box.AddString("index.html", "foo page"), "AddString should have no error.")
|
require.NoError(t, box.AddString("index.html", "foo page"), "AddString should have no error.")
|
||||||
|
|
||||||
handler := createRoutes("/foobar", &handler{mockedClient, true, box})
|
handler := createRoutes("/foobar", &handler{mockedClient, box})
|
||||||
req, err := http.NewRequest("GET", "/foobar/", nil)
|
req, err := http.NewRequest("GET", "/foobar/", nil)
|
||||||
require.NoError(t, err, "NewRequest should not return an error.")
|
require.NoError(t, err, "NewRequest should not return an error.")
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@@ -287,7 +343,7 @@ func Test_createRoutes_foobar_file(t *testing.T) {
|
|||||||
box := packr.NewBox("./virtual")
|
box := packr.NewBox("./virtual")
|
||||||
require.NoError(t, box.AddString("/test", "test page"), "AddString should have no error.")
|
require.NoError(t, box.AddString("/test", "test page"), "AddString should have no error.")
|
||||||
|
|
||||||
handler := createRoutes("/foobar", &handler{mockedClient, true, box})
|
handler := createRoutes("/foobar", &handler{mockedClient, box})
|
||||||
req, err := http.NewRequest("GET", "/foobar/test", nil)
|
req, err := http.NewRequest("GET", "/foobar/test", nil)
|
||||||
require.NoError(t, err, "NewRequest should not return an error.")
|
require.NoError(t, err, "NewRequest should not return an error.")
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@@ -300,7 +356,7 @@ func Test_createRoutes_version(t *testing.T) {
|
|||||||
mockedClient := new(MockedClient)
|
mockedClient := new(MockedClient)
|
||||||
box := packr.NewBox("./virtual")
|
box := packr.NewBox("./virtual")
|
||||||
|
|
||||||
handler := createRoutes("/", &handler{mockedClient, true, box})
|
handler := createRoutes("/", &handler{mockedClient, box})
|
||||||
req, err := http.NewRequest("GET", "/version", nil)
|
req, err := http.NewRequest("GET", "/version", nil)
|
||||||
require.NoError(t, err, "NewRequest should not return an error.")
|
require.NoError(t, err, "NewRequest should not return an error.")
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
|
|||||||
112
package.json
@@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "dozzle",
|
"name": "dozzle",
|
||||||
"version": "1.22.0",
|
"version": "2.0.0",
|
||||||
"description": "Realtime log viewer for docker containers. ",
|
"description": "Realtime log viewer for docker containers. ",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prestart": "npm run clean",
|
"prestart": "yarn clean",
|
||||||
"start": "DOCKER_API_VERSION=1.38 concurrently 'npm run watch-server' 'npm run watch-assets'",
|
"start": "npm-run-all -p watch:*",
|
||||||
"watch-assets": "npx parcel watch --no-source-maps --public-url '__BASE__' assets/index.html -d static",
|
"watch:assets": "webpack --mode=development --watch",
|
||||||
"watch-server": "reflex -c .reflex",
|
"watch:server": "reflex -c .reflex",
|
||||||
"prebuild": "npm run clean",
|
"prebuild": "yarn clean",
|
||||||
"build": "npx parcel build --no-source-maps --public-url '__BASE__' assets/index.html -d static",
|
"build": "yarn webpack --mode=production",
|
||||||
"clean": "rm -rf static/ a_main-packr.go",
|
"clean": "rm -rf static/ a_main-packr.go",
|
||||||
"release": "release-it",
|
"release": "release-it",
|
||||||
"test": "jest",
|
"test": "TZ=UTC jest",
|
||||||
"integration": "docker-compose -f integration/docker-compose.test.yml run --rm integration"
|
"integration": "docker-compose -f integration/docker-compose.test.yml up --build --force-recreate integration"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -26,42 +26,54 @@
|
|||||||
"homepage": "https://github.com/amir20/dozzle#readme",
|
"homepage": "https://github.com/amir20/dozzle#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-to-html": "^0.6.14",
|
"ansi-to-html": "^0.6.14",
|
||||||
"buefy": "^0.8.15",
|
"buefy": "^0.8.20",
|
||||||
"bulma": "^0.8.1",
|
"bulma": "^0.9.0",
|
||||||
"caniuse-lite": "^1.0.30001040",
|
"date-fns": "^2.14.0",
|
||||||
"date-fns": "^2.12.0",
|
"dompurify": "^2.0.12",
|
||||||
"hotkeys-js": "^3.7.6",
|
"hotkeys-js": "^3.8.1",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"semver": "^7.2.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
|
"semver": "^7.3.2",
|
||||||
"splitpanes": "^2.2.1",
|
"splitpanes": "^2.2.1",
|
||||||
"store": "^2.0.12",
|
"store": "^2.0.12",
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue-meta": "^2.3.3",
|
"vue-meta": "^2.4.0",
|
||||||
"vue-router": "^3.1.6",
|
"vue-router": "^3.3.4",
|
||||||
"vuex": "^3.1.3"
|
"vuex": "^3.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.9.0",
|
"@babel/core": "^7.10.3",
|
||||||
"@babel/plugin-transform-runtime": "^7.9.0",
|
"@babel/plugin-transform-runtime": "^7.10.3",
|
||||||
"@vue/component-compiler-utils": "^3.1.2",
|
"@vue/component-compiler-utils": "^3.1.2",
|
||||||
"@vue/test-utils": "^1.0.0-beta.33",
|
"@vue/test-utils": "^1.0.3",
|
||||||
"babel-core": "^7.0.0-bridge.0",
|
"babel-core": "^7.0.0-bridge.0",
|
||||||
"babel-jest": "^25.3.0",
|
"babel-jest": "^26.1.0",
|
||||||
"concurrently": "^5.1.0",
|
"babel-preset-env": "^1.7.0",
|
||||||
|
"caniuse-lite": "^1.0.30001090",
|
||||||
|
"css-loader": "^3.6.0",
|
||||||
"eventsourcemock": "^2.0.0",
|
"eventsourcemock": "^2.0.0",
|
||||||
"husky": "^4.2.4",
|
"html-webpack-plugin": "^4.3.0",
|
||||||
"jest": "^25.3.0",
|
"husky": "^4.2.5",
|
||||||
|
"jest": "^26.1.0",
|
||||||
"jest-serializer-vue": "^2.0.2",
|
"jest-serializer-vue": "^2.0.2",
|
||||||
"lint-staged": "^10.1.3",
|
"lint-staged": "^10.2.11",
|
||||||
"mockdate": "^2.0.5",
|
"mini-css-extract-plugin": "^0.9.0",
|
||||||
"node-fetch": "^2.6.0",
|
"npm-run-all": "^4.1.5",
|
||||||
"parcel-bundler": "^1.12.4",
|
"postcss-cssnext": "^3.1.0",
|
||||||
"prettier": "^2.0.4",
|
"postcss-import": "^12.0.1",
|
||||||
"release-it": "^13.5.2",
|
"postcss-loader": "^3.0.0",
|
||||||
"sass": "^1.26.3",
|
"prettier": "^2.0.5",
|
||||||
|
"release-it": "^13.6.3",
|
||||||
|
"sass": "^1.26.9",
|
||||||
|
"sass-loader": "^8.0.2",
|
||||||
"vue-hot-reload-api": "^2.3.4",
|
"vue-hot-reload-api": "^2.3.4",
|
||||||
"vue-jest": "^3.0.5",
|
"vue-jest": "^3.0.5",
|
||||||
"vue-template-compiler": "^2.6.11"
|
"vue-loader": "^15.9.3",
|
||||||
|
"vue-style-loader": "^4.1.2",
|
||||||
|
"vue-template-compiler": "^2.6.11",
|
||||||
|
"webpack": "^4.43.0",
|
||||||
|
"webpack-cli": "^3.3.12",
|
||||||
|
"webpack-pwa-manifest": "^4.2.0"
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
@@ -73,40 +85,6 @@
|
|||||||
"prettier --write"
|
"prettier --write"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"browserslist": [
|
|
||||||
">5%"
|
|
||||||
],
|
|
||||||
"alias": {
|
|
||||||
"vue": "./node_modules/vue/dist/vue.runtime.esm.js"
|
|
||||||
},
|
|
||||||
"jest": {
|
|
||||||
"clearMocks": true,
|
|
||||||
"moduleFileExtensions": [
|
|
||||||
"js",
|
|
||||||
"json",
|
|
||||||
"vue"
|
|
||||||
],
|
|
||||||
"coveragePathIgnorePatterns": [
|
|
||||||
"node_modules"
|
|
||||||
],
|
|
||||||
"testPathIgnorePatterns": [
|
|
||||||
"node_modules",
|
|
||||||
"<rootDir>/integration/"
|
|
||||||
],
|
|
||||||
"transformIgnorePatterns": [
|
|
||||||
"node_modules"
|
|
||||||
],
|
|
||||||
"watchPathIgnorePatterns": [
|
|
||||||
"<rootDir>/node_modules/"
|
|
||||||
],
|
|
||||||
"snapshotSerializers": [
|
|
||||||
"jest-serializer-vue"
|
|
||||||
],
|
|
||||||
"transform": {
|
|
||||||
".*\\.vue$": "vue-jest",
|
|
||||||
".+\\.js$": "babel-jest"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"release-it": {
|
"release-it": {
|
||||||
"github": {
|
"github": {
|
||||||
"release": true
|
"release": true
|
||||||
|
|||||||
200
routes.go
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createRoutes(base string, h *handler) *mux.Router {
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.Use(setCSPHeaders)
|
||||||
|
if base != "/" {
|
||||||
|
r.HandleFunc(base, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
http.Redirect(w, req, base+"/", http.StatusMovedPermanently)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
s := r.PathPrefix(base).Subrouter()
|
||||||
|
s.HandleFunc("/api/containers.json", h.listContainers)
|
||||||
|
s.HandleFunc("/api/logs/stream", h.streamLogs)
|
||||||
|
s.HandleFunc("/api/logs", h.fetchLogsBetweenDates)
|
||||||
|
s.HandleFunc("/api/events/stream", h.streamEvents)
|
||||||
|
s.HandleFunc("/version", h.version)
|
||||||
|
s.PathPrefix("/").Handler(http.StripPrefix(base, http.HandlerFunc(h.index)))
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func setCSPHeaders(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Security-Policy", "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline' fonts.googleapis.com; img-src 'self'; manifest-src 'self'; font-src fonts.gstatic.com; connect-src 'self' api.github.com; require-trusted-types-for 'script'")
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) index(w http.ResponseWriter, req *http.Request) {
|
||||||
|
fileServer := http.FileServer(h.box)
|
||||||
|
if h.box.Has(req.URL.Path) && req.URL.Path != "" && req.URL.Path != "/" {
|
||||||
|
fileServer.ServeHTTP(w, req)
|
||||||
|
} else {
|
||||||
|
text, err := h.box.FindString("index.html")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
tmpl, err := template.New("index.html").Parse(text)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
path := ""
|
||||||
|
if base != "/" {
|
||||||
|
path = base
|
||||||
|
}
|
||||||
|
|
||||||
|
data := struct {
|
||||||
|
Base string
|
||||||
|
Version string
|
||||||
|
}{path, version}
|
||||||
|
err = tmpl.Execute(w, data)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) listContainers(w http.ResponseWriter, r *http.Request) {
|
||||||
|
containers, err := h.client.ListContainers()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = json.NewEncoder(w).Encode(containers)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
|
||||||
|
|
||||||
|
from, _ := time.Parse(time.RFC3339, r.URL.Query().Get("from"))
|
||||||
|
to, _ := time.Parse(time.RFC3339, r.URL.Query().Get("to"))
|
||||||
|
id := r.URL.Query().Get("id")
|
||||||
|
|
||||||
|
messages, _ := h.client.ContainerLogsBetweenDates(r.Context(), id, from, to)
|
||||||
|
|
||||||
|
for _, m := range messages {
|
||||||
|
fmt.Fprintln(w, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) streamLogs(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id := r.URL.Query().Get("id")
|
||||||
|
if id == "" {
|
||||||
|
http.Error(w, "id is required", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f, ok := w.(http.Flusher)
|
||||||
|
if !ok {
|
||||||
|
http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
container, e := h.client.FindContainer(id)
|
||||||
|
if e != nil {
|
||||||
|
http.Error(w, e.Error(), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
messages, err := h.client.ContainerLogs(r.Context(), container.ID, tailSize, r.Header.Get("Last-Event-ID"))
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "text/event-stream")
|
||||||
|
w.Header().Set("Cache-Control", "no-cache")
|
||||||
|
w.Header().Set("Connection", "keep-alive")
|
||||||
|
w.Header().Set("X-Accel-Buffering", "no")
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case message, ok := <-messages:
|
||||||
|
if !ok {
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "data: %s\n", message)
|
||||||
|
if index := strings.IndexAny(message, " "); index != -1 {
|
||||||
|
id := message[:index]
|
||||||
|
if _, err := time.Parse(time.RFC3339Nano, id); err == nil {
|
||||||
|
fmt.Fprintf(w, "id: %s\n", id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "\n")
|
||||||
|
f.Flush()
|
||||||
|
case e := <-err:
|
||||||
|
if e == io.EOF {
|
||||||
|
log.Debugf("Container stopped: %v", container.ID)
|
||||||
|
fmt.Fprintf(w, "event: container-stopped\ndata: end of stream\n\n")
|
||||||
|
f.Flush()
|
||||||
|
} else {
|
||||||
|
log.Debugf("Error while reading from log stream: %v", e)
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.WithField("NumGoroutine", runtime.NumGoroutine()).Debug("runtime stats")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) {
|
||||||
|
f, ok := w.(http.Flusher)
|
||||||
|
if !ok {
|
||||||
|
http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "text/event-stream")
|
||||||
|
w.Header().Set("Cache-Control", "no-cache")
|
||||||
|
w.Header().Set("Connection", "keep-alive")
|
||||||
|
w.Header().Set("X-Accel-Buffering", "no")
|
||||||
|
|
||||||
|
ctx := r.Context()
|
||||||
|
messages, err := h.client.Events(ctx)
|
||||||
|
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case message, ok := <-messages:
|
||||||
|
if !ok {
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
switch message.Action {
|
||||||
|
case "connect", "disconnect", "create", "destroy", "start", "stop":
|
||||||
|
log.Debugf("Triggering docker event: %v", message.Action)
|
||||||
|
_, err := fmt.Fprintf(w, "event: containers-changed\ndata: %s\n\n", message.Action)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("Error while writing to event stream: %v", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
f.Flush()
|
||||||
|
default:
|
||||||
|
log.Debugf("Ignoring docker event: %v", message.Action)
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
break Loop
|
||||||
|
case <-err:
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) version(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintln(w, version)
|
||||||
|
}
|
||||||
78
webpack.config.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
const path = require("path");
|
||||||
|
const { VueLoaderPlugin } = require("vue-loader");
|
||||||
|
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||||
|
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||||
|
const WebpackPwaManifest = require("webpack-pwa-manifest");
|
||||||
|
|
||||||
|
module.exports = (env, argv) => ({
|
||||||
|
stats: { children: false, entrypoints: false, modules: false },
|
||||||
|
performance: {
|
||||||
|
maxAssetSize: 350000,
|
||||||
|
maxEntrypointSize: 600000,
|
||||||
|
},
|
||||||
|
devtool: argv.mode === "development" ? "inline-cheap-source-map" : false,
|
||||||
|
entry: ["./assets/main.js", "./assets/styles.scss"],
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, "./static"),
|
||||||
|
filename: "[name].js",
|
||||||
|
publicPath: "{{ .Base }}",
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.vue$/,
|
||||||
|
loader: "vue-loader",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(sass|scss|css)$/,
|
||||||
|
use: [
|
||||||
|
MiniCssExtractPlugin.loader,
|
||||||
|
{
|
||||||
|
loader: "css-loader",
|
||||||
|
query: {
|
||||||
|
importLoaders: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: "postcss-loader",
|
||||||
|
options: {
|
||||||
|
ident: "postcss",
|
||||||
|
plugins: (loader) => [
|
||||||
|
require("postcss-import")(),
|
||||||
|
require("postcss-cssnext")({
|
||||||
|
features: {
|
||||||
|
customProperties: { warnings: false },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sass-loader",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new VueLoaderPlugin(),
|
||||||
|
new MiniCssExtractPlugin(),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
hash: true,
|
||||||
|
template: "assets/index.ejs",
|
||||||
|
scriptLoading: "defer",
|
||||||
|
favicon: "assets/favicon.svg",
|
||||||
|
}),
|
||||||
|
new WebpackPwaManifest({
|
||||||
|
name: "Dozzle Log Viewer",
|
||||||
|
short_name: "Dozzle",
|
||||||
|
theme_color: "#222",
|
||||||
|
background_color: "#222",
|
||||||
|
display: "standalone",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
vue$: "vue/dist/vue.runtime.esm.js",
|
||||||
|
},
|
||||||
|
extensions: ["*", ".js", ".vue", ".json"],
|
||||||
|
},
|
||||||
|
});
|
||||||