Compare commits
313 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
144bcccc27 | ||
|
|
bc3d2a5c74 | ||
|
|
357b619bef | ||
|
|
12aadd673b | ||
|
|
6bde7be53c | ||
|
|
fc4a3d90d5 | ||
|
|
b9532b4eac | ||
|
|
920acf4256 | ||
|
|
64f744a5d4 | ||
|
|
be66ecf3de | ||
|
|
e065409952 | ||
|
|
f8554155e7 | ||
|
|
28a54b4478 | ||
|
|
738a011b2a | ||
|
|
e3cc51f54b | ||
|
|
05ce19e910 | ||
|
|
04f3f56aa3 | ||
|
|
971a8c2da4 | ||
|
|
069b5581d8 | ||
|
|
0ed7408d6d | ||
|
|
b835506069 | ||
|
|
711f4a74b9 | ||
|
|
0c172e34ae | ||
|
|
4a29f5deb8 | ||
|
|
fbf217b990 | ||
|
|
1078b70190 | ||
|
|
4ccccbc9c8 | ||
|
|
e2fa22148a | ||
|
|
70e5b358fc | ||
|
|
85695a2165 | ||
|
|
600602a976 | ||
|
|
9c5749d6b2 | ||
|
|
ce1df8ec22 | ||
|
|
cb75ebebbd | ||
|
|
3e5eae38c8 | ||
|
|
5ffc667b5d | ||
|
|
d54c1caff8 | ||
|
|
7cad2fabc2 | ||
|
|
02ab509e8d | ||
|
|
fbf4f4f7cb | ||
|
|
4ad2b5413b | ||
|
|
ef63b0bf2f | ||
|
|
56c3cebf44 | ||
|
|
d84c90670b | ||
|
|
b7e64a7599 | ||
|
|
1f8d23c8ee | ||
|
|
0ab802a304 | ||
|
|
039c55ccb6 | ||
|
|
5730aa6fee | ||
|
|
ed56513562 | ||
|
|
e0366fd928 | ||
|
|
a76dc67752 | ||
|
|
d56d27bf14 | ||
|
|
3550724d8a | ||
|
|
b8b92edee1 | ||
|
|
59be9a2a78 | ||
|
|
b852df68d6 | ||
|
|
bd2aea5207 | ||
|
|
194b31b36f | ||
|
|
dfc3254cf4 | ||
|
|
2ce28f81f6 | ||
|
|
f7e9ba348e | ||
|
|
d495c018f5 | ||
|
|
a160796bd0 | ||
|
|
743caf20d1 | ||
|
|
879ad0278a | ||
|
|
106dd88443 | ||
|
|
d4bd807b05 | ||
|
|
dfe232b529 | ||
|
|
e3f9142f7b | ||
|
|
2e3fa0ea78 | ||
|
|
da89162143 | ||
|
|
c84ba2523e | ||
|
|
a28d75aee5 | ||
|
|
500b390073 | ||
|
|
9314bc8b50 | ||
|
|
264f5b71e6 | ||
|
|
738e9cf17a | ||
|
|
713955a98c | ||
|
|
21950ed61b | ||
|
|
0420ff34ae | ||
|
|
76a138276f | ||
|
|
26b0a0b474 | ||
|
|
c7ce0036f4 | ||
|
|
532eeaf8fc | ||
|
|
0af00eeba4 | ||
|
|
74a37daa06 | ||
|
|
729da05a71 | ||
|
|
db63bb40ce | ||
|
|
85dcc7ed12 | ||
|
|
5867b3bc79 | ||
|
|
18c2ef3dc1 | ||
|
|
e4737cb10e | ||
|
|
648bc23258 | ||
|
|
f31210e295 | ||
|
|
27401a93fe | ||
|
|
521f9a08c1 | ||
|
|
389eba1599 | ||
|
|
bdb9dd6342 | ||
|
|
6c8f081a23 | ||
|
|
b25be0bbcd | ||
|
|
af2ad8447b | ||
|
|
e4760ed218 | ||
|
|
5bc8e90c41 | ||
|
|
2f613d99bf | ||
|
|
aaf3b92038 | ||
|
|
69e6f3e0e2 | ||
|
|
5d802c435d | ||
|
|
25df502e9d | ||
|
|
e196752a49 | ||
|
|
0c86813824 | ||
|
|
8238620360 | ||
|
|
ae1cbecfc4 | ||
|
|
ec3c1806b9 | ||
|
|
52eb3a5c67 | ||
|
|
d5a61b3f55 | ||
|
|
7c818b103b | ||
|
|
8ecad6555d | ||
|
|
2ac4425c58 | ||
|
|
59b9c01f10 | ||
|
|
011513b137 | ||
|
|
ec8a7ccc92 | ||
|
|
8bf9dfcf33 | ||
|
|
65d90246ac | ||
|
|
5a50a8e68a | ||
|
|
6c5f7e4e5e | ||
|
|
cda44a89c2 | ||
|
|
321c1292dc | ||
|
|
ec11be013b | ||
|
|
5e76716d28 | ||
|
|
66b663806d | ||
|
|
bddc24c830 | ||
|
|
e5d92a5ec7 | ||
|
|
2506ec8a7b | ||
|
|
62b4d1828a | ||
|
|
8eab5a6904 | ||
|
|
c2dd1f2f99 | ||
|
|
0bbd28d12b | ||
|
|
b7a603a388 | ||
|
|
f143fb4a9b | ||
|
|
494dddb1f7 | ||
|
|
386eb256e7 | ||
|
|
efba364f66 | ||
|
|
dd329fc776 | ||
|
|
b74493740d | ||
|
|
1eb6cbc2b6 | ||
|
|
cba41dcf4e | ||
|
|
83023ffd6f | ||
|
|
a06b42d248 | ||
|
|
90e36388d0 | ||
|
|
90b1331d7c | ||
|
|
bf64ac2fc9 | ||
|
|
228c059da7 | ||
|
|
ead45c8919 | ||
|
|
4c9dade88d | ||
|
|
1f8bb7706e | ||
|
|
88251cf388 | ||
|
|
dbfe6e157e | ||
|
|
e10db315fe | ||
|
|
32334c46af | ||
|
|
f77c9931c0 | ||
|
|
96ad0ffdd0 | ||
|
|
c1b140f562 | ||
|
|
6378405913 | ||
|
|
0ca6f9a480 | ||
|
|
c2f0fc669d | ||
|
|
bc5d80f4d4 | ||
|
|
d19c59122b | ||
|
|
2646fc08b4 | ||
|
|
06b7728699 | ||
|
|
8be95f4359 | ||
|
|
23effa0f37 | ||
|
|
0b3493c646 | ||
|
|
61fbd54ec9 | ||
|
|
64d95e7466 | ||
|
|
b480f03c73 | ||
|
|
9dd79e5b67 | ||
|
|
5fac064adb | ||
|
|
28c25acf64 | ||
|
|
0fdc6d4495 | ||
|
|
70b97d6243 | ||
|
|
1a4e54408b | ||
|
|
21e6b9e87c | ||
|
|
195f0da645 | ||
|
|
b18612942f | ||
|
|
ddb0c05173 | ||
|
|
590add10c1 | ||
|
|
984dbdf971 | ||
|
|
6c8828ffd7 | ||
|
|
9fdfabfb5d | ||
|
|
521b0bbbe8 | ||
|
|
02f995f859 | ||
|
|
4c01314286 | ||
|
|
e9231acc57 | ||
|
|
8977a71495 | ||
|
|
e6451cdcb6 | ||
|
|
f18e6d6adc | ||
|
|
7b05586a48 | ||
|
|
9de4bc8a8c | ||
|
|
3defe22eed | ||
|
|
d65d899056 | ||
|
|
b4f8b97b7d | ||
|
|
e3934e0422 | ||
|
|
a81415744a | ||
|
|
51a74691f9 | ||
|
|
17d6d1b2ca | ||
|
|
ce755f61dc | ||
|
|
2e4665e468 | ||
|
|
72b89c5ea5 | ||
|
|
f77fac2221 | ||
|
|
9b347f3dfb | ||
|
|
c584c565a3 | ||
|
|
6730814ffc | ||
|
|
d57ea5233c | ||
|
|
90afa43cef | ||
|
|
aac9905099 | ||
|
|
07723353d0 | ||
|
|
c6a3b8f61e | ||
|
|
3db60e3d50 | ||
|
|
b8d428f5b3 | ||
|
|
abca733e63 | ||
|
|
6bc133a643 | ||
|
|
280413c085 | ||
|
|
0bf265b001 | ||
|
|
c65d1f18ae | ||
|
|
eb8f492ffe | ||
|
|
5358af5005 | ||
|
|
5569c7f5b4 | ||
|
|
5caf5eebb8 | ||
|
|
8ffe95b4ab | ||
|
|
cca6b0971b | ||
|
|
0b45972692 | ||
|
|
84d626c75b | ||
|
|
380e65641e | ||
|
|
49a2bd918a | ||
|
|
e35734d73a | ||
|
|
9103124bfa | ||
|
|
c440aaf1a1 | ||
|
|
aaf3cd84f0 | ||
|
|
cc1e7d0cbe | ||
|
|
4012145a3e | ||
|
|
b3cfec8402 | ||
|
|
3a555a8b04 | ||
|
|
99a023e3b1 | ||
|
|
f239d077a2 | ||
|
|
f822bd5ccd | ||
|
|
3c96572fb0 | ||
|
|
4daccddab5 | ||
|
|
41415e6731 | ||
|
|
2bbbb5f7a5 | ||
|
|
c89e70d697 | ||
|
|
cbba0fa0ad | ||
|
|
a60634ab1b | ||
|
|
0a9072cf53 | ||
|
|
dfaea1c95a | ||
|
|
a331b6a20b | ||
|
|
3d710118e1 | ||
|
|
be24567d74 | ||
|
|
cf7594af38 | ||
|
|
0527d58959 | ||
|
|
36984d0787 | ||
|
|
f6ec3126a4 | ||
|
|
767fc4fde3 | ||
|
|
137dc490ce | ||
|
|
4d7a682ecf | ||
|
|
7b4ca4e4fa | ||
|
|
232ec7d27a | ||
|
|
bba34aa582 | ||
|
|
70a40cd7e4 | ||
|
|
b90f79449b | ||
|
|
8aea96d336 | ||
|
|
ed80bff954 | ||
|
|
013aefa969 | ||
|
|
986fe38254 | ||
|
|
9c9172a180 | ||
|
|
7f9fd2c80c | ||
|
|
c8202d1238 | ||
|
|
91b02bcfce | ||
|
|
46c4dde573 | ||
|
|
52729fa980 | ||
|
|
915cff92c6 | ||
|
|
b591595285 | ||
|
|
0486db9169 | ||
|
|
4a1c699e93 | ||
|
|
83e4175fca | ||
|
|
b8bec9d2f0 | ||
|
|
802744d132 | ||
|
|
b4e0afdb2c | ||
|
|
7b7fd11bc7 | ||
|
|
b8beb4eba7 | ||
|
|
e3e28e7e3d | ||
|
|
509de3b89c | ||
|
|
f3aef59740 | ||
|
|
27d351f21c | ||
|
|
e98b482b73 | ||
|
|
70a4dc5734 | ||
|
|
cda7318e45 | ||
|
|
6b098e1233 | ||
|
|
a6f28488ee | ||
|
|
f4091ba95c | ||
|
|
d597f4d3f7 | ||
|
|
f9bbf7081f | ||
|
|
42f2028c29 | ||
|
|
0b09d6c785 | ||
|
|
33430b2974 | ||
|
|
ac64579bcd | ||
|
|
dd7929ec59 | ||
|
|
17fe73bd27 | ||
|
|
a607c2d021 | ||
|
|
a840f19b20 | ||
|
|
ec4f3c0339 | ||
|
|
721e30385e | ||
|
|
ee95bc75fb |
2
.github/.kodiak.toml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
version = 1
|
||||
merge.notify_on_conflict = false
|
||||
40
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "automerge"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "automerge"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: gomod
|
||||
directory: "/"
|
||||
labels:
|
||||
- "gomod"
|
||||
- "dependencies"
|
||||
- "automerge"
|
||||
schedule:
|
||||
interval: daily
|
||||
- package-ecosystem: npm
|
||||
directory: "/"
|
||||
labels:
|
||||
- "npm"
|
||||
- "dependencies"
|
||||
- "automerge"
|
||||
schedule:
|
||||
interval: daily
|
||||
- package-ecosystem: npm
|
||||
directory: "/integration"
|
||||
labels:
|
||||
- "npm"
|
||||
- "dependencies"
|
||||
- "automerge"
|
||||
schedule:
|
||||
interval: daily
|
||||
67
.github/workflows/codeql-analysis.yml
vendored
@@ -1,67 +0,0 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '42 21 * * 0'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go', 'javascript' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
63
.github/workflows/deploy.yml
vendored
@@ -9,9 +9,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v2.3.4
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v2.1.5
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
- name: Run Tests
|
||||
@@ -21,11 +21,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v2.1.3
|
||||
with:
|
||||
go-version: 1.16.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v2.3.4
|
||||
- name: Run Go Tests with Coverage
|
||||
run: make test SKIP_ASSET=1
|
||||
int-test:
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v2.3.4
|
||||
- name: Build images
|
||||
run: docker-compose -f integration/docker-compose.test.yml build
|
||||
- name: Run tests
|
||||
@@ -43,28 +43,55 @@ jobs:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: crazy-max/ghaction-docker-meta@v3.3.0
|
||||
with:
|
||||
images: amir20/dozzle
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
uses: docker/setup-qemu-action@v1.1.0
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Available platforms
|
||||
run: echo ${{ steps.buildx.outputs.platforms }}
|
||||
- name: Docker Login
|
||||
run: docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Run Buildx
|
||||
run: make publish
|
||||
uses: docker/setup-buildx-action@v1.3.0
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1.9.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2.4.0
|
||||
with:
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm64/v8
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
build-args: TAG=${{ steps.meta.outputs.version }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache-new
|
||||
- # Temp fix
|
||||
# https://github.com/docker/build-push-action/issues/252
|
||||
# https://github.com/moby/buildkit/issues/1896
|
||||
name: Move cache
|
||||
run: |
|
||||
rm -rf /tmp/.buildx-cache
|
||||
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
|
||||
git-release:
|
||||
needs: [buildx]
|
||||
name: Github Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v2.1.5
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
- name: Release to Github
|
||||
|
||||
10
.github/workflows/test.yml
vendored
@@ -6,9 +6,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v2.3.4
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v2.1.5
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
- name: Run Tests
|
||||
@@ -18,11 +18,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v2.1.3
|
||||
with:
|
||||
go-version: 1.16.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v2.3.4
|
||||
- name: Run Go Tests with Coverage
|
||||
run: make test SKIP_ASSET=1
|
||||
int-test:
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v2.3.4
|
||||
- name: Build images
|
||||
run: docker-compose -f integration/docker-compose.test.yml build
|
||||
- name: Run tests
|
||||
|
||||
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' -G '*_test.go' -s -- go run main.go --level debug
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Build assets
|
||||
FROM node:current-alpine as node
|
||||
FROM node:16-alpine as node
|
||||
|
||||
RUN apk add --no-cache git openssh python make g++ util-linux
|
||||
RUN apk add --no-cache git openssh make g++ util-linux
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
|
||||
20
Makefile
@@ -1,15 +1,7 @@
|
||||
TAG := $(shell git describe --tags)
|
||||
PLATFROMS := linux/amd64,linux/arm/v7,linux/arm64/v8
|
||||
|
||||
|
||||
.PHONY: publish
|
||||
publish:
|
||||
docker buildx build --build-arg TAG=$(TAG) --platform $(PLATFROMS) -t amir20/dozzle:latest -t amir20/dozzle:$(TAG) --push .
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf static
|
||||
@go clean
|
||||
@go clean -i
|
||||
|
||||
.PHONY: static
|
||||
static:
|
||||
@@ -25,8 +17,18 @@ fake_static:
|
||||
test: fake_static
|
||||
go test -cover ./...
|
||||
|
||||
.PHONY: build
|
||||
build: static
|
||||
CGO_ENABLED=0 go build -ldflags "-s -w"
|
||||
|
||||
.PHONY: docker
|
||||
docker:
|
||||
@docker build -t amir20/dozzle .
|
||||
|
||||
.PHONY: dev
|
||||
dev:
|
||||
yarn dev
|
||||
|
||||
.PHONY: int
|
||||
int:
|
||||
docker-compose -f integration/docker-compose.test.yml up --build --force-recreate --exit-code-from integration
|
||||
|
||||
92
README.md
@@ -1,32 +1,42 @@
|
||||
# Dozzle - [dozzle.dev](https://dozzle.dev/)
|
||||
|
||||
Dozzle is a small lightweight application with a web based interface to monitor Docker logs. It doesn’t store any log files. It is for live monitoring of your container logs only.
|
||||
|
||||

|
||||
|
||||
[](https://goreportcard.com/report/github.com/amir20/dozzle)
|
||||
[](https://hub.docker.com/r/amir20/dozzle/)
|
||||
[](https://hub.docker.com/r/amir20/dozzle/)
|
||||
[](https://hub.docker.com/r/amir20/dozzle/)
|
||||

|
||||
|
||||
# Dozzle - [dozzle.dev](https://dozzle.dev/)
|
||||
## Features
|
||||
|
||||
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.
|
||||
- Intelligent fuzzy search for container names 🤖
|
||||
- Search logs using regex 🔦
|
||||
- Small memory footprint 🏎
|
||||
- Split screen for viewing multiple logs
|
||||
- Download logs easy
|
||||
- Live stats with memory and CPU usage
|
||||
- Authentication with username and password 🚨
|
||||
|
||||
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.
|
||||
Dozzle should work for most. It has been tested with hundreds of containers. However, it doesn't support offline searching. Products like [Loggly](https://www.loggly.com), [Papertrail](https://papertrailapp.com) or [Kibana](https://www.elastic.co/products/kibana) are more suited for full search capabilities.
|
||||
|
||||
Dozzle doesn't cost any money. Dozzle aims to stay simple, small and free.
|
||||
Dozzle doesn't cost any money and aims to focus on real-time debugging.
|
||||
|
||||

|
||||
|
||||
## Getting dozzle
|
||||
## Getting Dozzle
|
||||
|
||||
Dozzle is a very small Docker container (4 MB compressed). Pull the latest release from the index:
|
||||
|
||||
$ docker pull amir20/dozzle:latest
|
||||
|
||||
## Using dozzle
|
||||
## Using Dozzle
|
||||
|
||||
The simplest way to use dozzle is to run the docker container. Also, mount the Docker Unix socket with `--volume` to `/var/run/docker.sock`:
|
||||
|
||||
$ docker run --name dozzle -d --volume=/var/run/docker.sock:/var/run/docker.sock -p 8888:8080 amir20/dozzle:latest
|
||||
|
||||
dozzle will be available at [http://localhost:8888/](http://localhost:8888/). You can change `-p 8888:8080` to any port. For example, if you want to view dozzle over port 4040 then you would do `-p 4040:8080`.
|
||||
Dozzle will be available at [http://localhost:8888/](http://localhost:8888/). You can change `-p 8888:8080` to any port. For example, if you want to view dozzle over port 4040 then you would do `-p 4040:8080`.
|
||||
|
||||
### With Docker swarm
|
||||
|
||||
@@ -51,7 +61,7 @@ dozzle will be available at [http://localhost:8888/](http://localhost:8888/). Yo
|
||||
|
||||
#### Security
|
||||
|
||||
dozzle doesn't support authentication out of the box. You can control the device dozzle binds to by passing `--addr` parameter. For example,
|
||||
You can control the device Dozzle binds to by passing `--addr` parameter. For example,
|
||||
|
||||
$ docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8888:1224 amir20/dozzle:latest --addr localhost:1224
|
||||
|
||||
@@ -61,38 +71,54 @@ If you wish to restrict the containers shown you can pass the `--filter` paramet
|
||||
|
||||
$ docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8888:1224 amir20/dozzle:latest --filter name=foo
|
||||
|
||||
this would then only allow you to view containers with a name starting with "foo". You can use other filters like `status` as well, please check the official docker [command line docs](https://docs.docker.com/engine/reference/commandline/ps/#filtering) for available filters.
|
||||
this would then only allow you to view containers with a name starting with "foo". You can use other filters like `status` as well, please check the official docker [command line docs](https://docs.docker.com/engine/reference/commandline/ps/#filtering) for available filters. Multiple `--filter` arguments can be provided.
|
||||
|
||||
#### Authentication
|
||||
|
||||
Dozzle supports a very simple authentication out of the box with just username and password. You should deploy using SSL to keep the credentials safe. See configuration to use `--username` and `--password`.
|
||||
|
||||
#### Changing base URL
|
||||
|
||||
dozzle by default mounts to "/". If you want to control the base path you can use the `--base` option. For example, if you want to mount at "/foobar",
|
||||
Dozzle by default mounts to "/". If you want to control the base path you can use the `--base` option. For example, if you want to mount at "/foobar",
|
||||
then you can override by using `--base /foobar`. See env variables below for using `DOZZLE_BASE` to change this.
|
||||
|
||||
$ docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8080:8080 amir20/dozzle:latest --base /foobar
|
||||
|
||||
dozzle will be available at [http://localhost:8080/foobar/](http://localhost:8080/foobar/).
|
||||
Dozzle will be available at [http://localhost:8080/foobar/](http://localhost:8080/foobar/).
|
||||
|
||||
#### Analytics collected
|
||||
|
||||
Dozzle collects anonymous user configurations using Google Analytics. Why? Dozzle is an open source project with no funding. As a result, there is no time to do user studies of Dozzle. Analytics is collected to prioritize features and fixes based on how people use Dozzle. This data is completely public and can be viewed live using [ Data Studio dashboard](https://datastudio.google.com/s/naeIu0MiWsY).
|
||||
|
||||
If you do not want to be tracked at all, see the `--no-analytics` flag below.
|
||||
|
||||
#### Environment variables and configuration
|
||||
|
||||
Dozzle follows the [12-factor](https://12factor.net/) model. Configurations can use the CLI flags or enviroment variables. The table below outlines all supported options and their respective env vars.
|
||||
Dozzle follows the [12-factor](https://12factor.net/) model. Configurations can use the CLI flags or environment variables. The table below outlines all supported options and their respective env vars.
|
||||
|
||||
| Flag | Env Variable | Default |
|
||||
| ------------ | -------------------- | ------- |
|
||||
| `--addr` | `DOZZLE_ADDR` | `:8080` |
|
||||
| `--base` | `DOZZLE_BASE` | `/` |
|
||||
| `--level` | `DOZZLE_LEVEL` | `info` |
|
||||
| n/a | `DOCKER_API_VERSION` | not set |
|
||||
| `--tailSize` | `DOZZLE_TAILSIZE` | `300` |
|
||||
| `--filter` | `DOZZLE_FILTER` | `""` |
|
||||
| Flag | Env Variable | Default |
|
||||
| ---------------- | --------------------- | ------- |
|
||||
| `--addr` | `DOZZLE_ADDR` | `:8080` |
|
||||
| `--base` | `DOZZLE_BASE` | `/` |
|
||||
| `--level` | `DOZZLE_LEVEL` | `info` |
|
||||
| n/a | `DOCKER_API_VERSION` | not set |
|
||||
| `--tailSize` | `DOZZLE_TAILSIZE` | `300` |
|
||||
| `--filter` | `DOZZLE_FILTER` | `""` |
|
||||
| `--username` | `DOZZLE_USERNAME` | `""` |
|
||||
| `--password` | `DOZZLE_PASSWORD` | `""` |
|
||||
| `--key` | `DOZZLE_KEY` | `""` |
|
||||
| `--no-analytics` | `DOZZLE_NO_ANALYTICS` | false |
|
||||
|
||||
Note: When using username and password `DOZZLE_KEY` is required for session management.
|
||||
|
||||
## 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 server 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 sends the `X-Accel-Buffering: no` header which should stop reverse proxies buffering. However, some proxies may ignore this header. In those cases, you need to explicitly disable any buffering.
|
||||
Dozzle uses Server Sent Events (SSE) which connects to a server 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 sends the `X-Accel-Buffering: no` header which should stop reverse proxies buffering. However, some proxies may ignore this header. In those cases, you need to explicitly disable any buffering.
|
||||
|
||||
Below is an example with nginx and using `proxy_pass` to disable buffering.
|
||||
Below is an example with nginx and using `proxy_pass` to disable buffering.
|
||||
|
||||
```
|
||||
server {
|
||||
@@ -111,19 +137,29 @@ Dozzle follows the [12-factor](https://12factor.net/) model. Configurations can
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
</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:
|
||||
Dozzle does collect some analytics. Analytics is anonymous usage tracking of the features which are used the most. See the section above on how to disable any analytic collection.
|
||||
|
||||
- Allow connect to `api.github.com` to fetch most recent version.
|
||||
- Only allow `<script>` and `<style>` files from `self`
|
||||
In the browser, Dozzle has a [strict](https://github.com/amir20/dozzle/blob/master/web/csp.go#L9) Content Security Policy which only allows the following policies:
|
||||
|
||||
- Allow connect to `api.github.com` to fetch most recent version.
|
||||
- Only allow `<script>` and `<style>` files from `self`
|
||||
|
||||
Dozzle opens all links with `rel="noopener"`.
|
||||
|
||||
Dozzle opens all links with `rel="noopener"`.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>We have tools that uses Dozzle when a new container is created. How can I get a direct link to a container by name?</summary>
|
||||
|
||||
Dozzle has a [special route](https://github.com/amir20/dozzle/blob/master/assets/pages/Show.vue) that can be used to search containers by name and then forward to that container. For example, if you have a container with name `"foo.bar"` and id `abc123`, you can send your users to `/show?name=foo.bar` which will be forwarded to `/container/abc123`.
|
||||
|
||||
</details>
|
||||
|
||||
## License
|
||||
|
||||
|
||||
55
analytics/ga.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package analytics
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func SendStartEvent(se StartEvent) error {
|
||||
postBody := map[string]interface{}{
|
||||
"client_id": se.ClientId,
|
||||
"events": []map[string]interface{}{
|
||||
{
|
||||
"name": "start",
|
||||
"params": se,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
jsonValue, err := json.Marshal(postBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", "https://www.google-analytics.com/mp/collect", bytes.NewBuffer(jsonValue))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q := req.URL.Query()
|
||||
q.Add("measurement_id", "G-S6NT05VXK9")
|
||||
q.Add("api_secret", "7FFhe65HQK-bXvujpQMquQ")
|
||||
req.URL.RawQuery = q.Encode()
|
||||
|
||||
response, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode/100 != 2 {
|
||||
dump, err := httputil.DumpResponse(response, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("%v", string(dump))
|
||||
return fmt.Errorf("google analytics returned non-2xx status code: %v", response.Status)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
11
analytics/types.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package analytics
|
||||
|
||||
type StartEvent struct {
|
||||
ClientId string `json:"-"`
|
||||
Version string `json:"version"`
|
||||
FilterLength int `json:"filterLength"`
|
||||
CustomAddress bool `json:"customAddress"`
|
||||
CustomBase bool `json:"customBase"`
|
||||
TailSize int `json:"tailSize"`
|
||||
Protected bool `json:"protected"`
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<main>
|
||||
<mobile-menu v-if="isMobile"></mobile-menu>
|
||||
<mobile-menu v-if="isMobile && !authorizationNeeded"></mobile-menu>
|
||||
|
||||
<splitpanes @resized="onResized($event)">
|
||||
<pane min-size="10" :size="settings.menuWidth" v-if="!isMobile && !collapseNav">
|
||||
<pane min-size="10" :size="settings.menuWidth" v-if="!authorizationNeeded && !isMobile && !collapseNav">
|
||||
<side-menu @search="showFuzzySearch"></side-menu>
|
||||
</pane>
|
||||
<pane min-size="10">
|
||||
@@ -11,15 +11,17 @@
|
||||
<pane class="has-min-height router-view">
|
||||
<router-view></router-view>
|
||||
</pane>
|
||||
<pane v-for="other in activeContainers" :key="other.id" v-if="!isMobile">
|
||||
<log-container
|
||||
:id="other.id"
|
||||
show-title
|
||||
scrollable
|
||||
closable
|
||||
@close="removeActiveContainer(other)"
|
||||
></log-container>
|
||||
</pane>
|
||||
<template v-if="!isMobile">
|
||||
<pane v-for="other in activeContainers" :key="other.id">
|
||||
<log-container
|
||||
:id="other.id"
|
||||
show-title
|
||||
scrollable
|
||||
closable
|
||||
@close="removeActiveContainer(other)"
|
||||
></log-container>
|
||||
</pane>
|
||||
</template>
|
||||
</splitpanes>
|
||||
</pane>
|
||||
</splitpanes>
|
||||
@@ -28,7 +30,7 @@
|
||||
class="button is-small is-rounded is-settings-control"
|
||||
:class="{ collapsed: collapseNav }"
|
||||
id="hide-nav"
|
||||
v-if="!isMobile"
|
||||
v-if="!isMobile && !authorizationNeeded"
|
||||
>
|
||||
<span class="icon">
|
||||
<icon :name="collapseNav ? 'chevron-right' : 'chevron-left'"></icon>
|
||||
@@ -83,6 +85,7 @@ export default {
|
||||
}
|
||||
this.menuWidth = this.settings.menuWidth;
|
||||
hotkeys("command+k, ctrl+k", (event, handler) => {
|
||||
event.preventDefault();
|
||||
this.showFuzzySearch();
|
||||
});
|
||||
},
|
||||
@@ -106,7 +109,7 @@ export default {
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(["isMobile", "settings", "containers"]),
|
||||
...mapState(["isMobile", "settings", "containers", "authorizationNeeded"]),
|
||||
...mapGetters(["visibleContainers", "activeContainers"]),
|
||||
hasSmallerScrollbars() {
|
||||
return this.settings.smallerScrollbars;
|
||||
|
||||
@@ -33,16 +33,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from "vuex";
|
||||
|
||||
import LogViewerWithSource from "./LogViewerWithSource";
|
||||
import ScrollableView from "./ScrollableView";
|
||||
import ContainerTitle from "./ContainerTitle";
|
||||
import ContainerStat from "./ContainerStat";
|
||||
import Icon from "./Icon";
|
||||
import config from "../store/config";
|
||||
import containerMixin from "./mixins/container";
|
||||
|
||||
export default {
|
||||
mixins: [containerMixin],
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
@@ -69,10 +69,6 @@ export default {
|
||||
Icon,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(["allContainersById"]),
|
||||
container() {
|
||||
return this.allContainersById[this.id];
|
||||
},
|
||||
base() {
|
||||
return config.base;
|
||||
},
|
||||
|
||||
@@ -8,6 +8,7 @@ import LogViewer from "./LogViewer.vue";
|
||||
|
||||
jest.mock("lodash.debounce", () =>
|
||||
jest.fn((fn) => {
|
||||
fn.cancel = () => {};
|
||||
return fn;
|
||||
})
|
||||
);
|
||||
@@ -27,16 +28,24 @@ describe("<LogEventSource />", () => {
|
||||
debounce.mockClear();
|
||||
});
|
||||
|
||||
function createLogEventSource(searchFilter = null) {
|
||||
function createLogEventSource({ hourStyle = "auto", searchFilter = null } = {}) {
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(Vuex);
|
||||
|
||||
localVue.component("log-viewer", LogViewer);
|
||||
|
||||
const state = { searchFilter, settings: { size: "medium", showTimestamp: true } };
|
||||
const state = { searchFilter, settings: { size: "medium", showTimestamp: true, hourStyle } };
|
||||
const getters = {
|
||||
allContainersById() {
|
||||
return {
|
||||
abc: { state: "running" },
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const store = new Vuex.Store({
|
||||
state,
|
||||
getters,
|
||||
});
|
||||
|
||||
return mount(LogEventSource, {
|
||||
@@ -57,22 +66,25 @@ describe("<LogEventSource />", () => {
|
||||
});
|
||||
|
||||
test("should connect to EventSource", async () => {
|
||||
shallowMount(LogEventSource);
|
||||
sources["/api/logs/stream?id=abc"].emitOpen();
|
||||
expect(sources["/api/logs/stream?id=abc"].readyState).toBe(1);
|
||||
const wrapper = createLogEventSource();
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].emitOpen();
|
||||
expect(sources["/api/logs/stream?id=abc&lastEventId="].readyState).toBe(1);
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
test("should close EventSource", async () => {
|
||||
const wrapper = createLogEventSource();
|
||||
sources["/api/logs/stream?id=abc"].emitOpen();
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].emitOpen();
|
||||
wrapper.destroy();
|
||||
expect(sources["/api/logs/stream?id=abc"].readyState).toBe(2);
|
||||
expect(sources["/api/logs/stream?id=abc&lastEventId="].readyState).toBe(2);
|
||||
});
|
||||
|
||||
test("should parse messages", async () => {
|
||||
const wrapper = createLogEventSource();
|
||||
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&lastEventId="].emitOpen();
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({
|
||||
data: `2019-06-12T10:55:42.459034602Z "This is a message."`,
|
||||
});
|
||||
|
||||
const [message, _] = wrapper.vm.messages;
|
||||
const { key, ...messageWithoutKey } = message;
|
||||
@@ -81,15 +93,15 @@ describe("<LogEventSource />", () => {
|
||||
expect(messageWithoutKey).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"date": 2019-06-12T10:55:42.459Z,
|
||||
"message": " \\"This is a message.\\"",
|
||||
"message": "\\"This is a message.\\"",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
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` });
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].emitOpen();
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({ data: `2020-04-27T12:35:43.272974324+02:00 xxxxx` });
|
||||
|
||||
const [message, _] = wrapper.vm.messages;
|
||||
const { key, ...messageWithoutKey } = message;
|
||||
@@ -98,15 +110,17 @@ describe("<LogEventSource />", () => {
|
||||
expect(messageWithoutKey).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"date": 2020-04-27T10:35:43.272Z,
|
||||
"message": " xxxxx",
|
||||
"message": "xxxxx",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test("should pass messages to slot", async () => {
|
||||
const wrapper = createLogEventSource();
|
||||
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&lastEventId="].emitOpen();
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({
|
||||
data: `2019-06-12T10:55:42.459034602Z "This is a message."`,
|
||||
});
|
||||
const [message, _] = wrapper.findComponent(LogViewer).vm.messages;
|
||||
|
||||
const { key, ...messageWithoutKey } = message;
|
||||
@@ -116,7 +130,7 @@ describe("<LogEventSource />", () => {
|
||||
expect(messageWithoutKey).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"date": 2019-06-12T10:55:42.459Z,
|
||||
"message": " \\"This is a message.\\"",
|
||||
"message": "\\"This is a message.\\"",
|
||||
}
|
||||
`);
|
||||
});
|
||||
@@ -138,61 +152,93 @@ describe("<LogEventSource />", () => {
|
||||
|
||||
test("should render messages", async () => {
|
||||
const wrapper = createLogEventSource();
|
||||
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&lastEventId="].emitOpen();
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({
|
||||
data: `2019-06-12T10:55:42.459034602Z "This is a message."`,
|
||||
});
|
||||
|
||||
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 message."</span></li>
|
||||
<li><span class="date"><time datetime="2019-06-12T10:55:42.459Z">today at 10:55:42 AM</time></span> <span class="text">"This is a message."</span></li>
|
||||
</ul>
|
||||
`);
|
||||
});
|
||||
|
||||
test("should render messages with color", async () => {
|
||||
const wrapper = createLogEventSource();
|
||||
sources["/api/logs/stream?id=abc"].emitOpen();
|
||||
sources["/api/logs/stream?id=abc"].emitMessage({
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].emitOpen();
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({
|
||||
data: `2019-06-12T10:55:42.459034602Z \x1b[30mblack\x1b[37mwhite`,
|
||||
});
|
||||
|
||||
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"> <span style="color:#000">black<span style="color:#AAA">white</span></span></span></li>
|
||||
<li><span class="date"><time datetime="2019-06-12T10:55:42.459Z">today at 10:55:42 AM</time></span> <span class="text"><span style="color:#000">black<span style="color:#AAA">white</span></span></span></li>
|
||||
</ul>
|
||||
`);
|
||||
});
|
||||
|
||||
test("should render messages with html entities", async () => {
|
||||
const wrapper = createLogEventSource();
|
||||
sources["/api/logs/stream?id=abc"].emitOpen();
|
||||
sources["/api/logs/stream?id=abc"].emitMessage({
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].emitOpen();
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({
|
||||
data: `2019-06-12T10:55:42.459034602Z <test>foo bar</test>`,
|
||||
});
|
||||
|
||||
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"> <test>foo bar</test></span></li>
|
||||
<li><span class="date"><time datetime="2019-06-12T10:55:42.459Z">today at 10:55:42 AM</time></span> <span class="text"><test>foo bar</test></span></li>
|
||||
</ul>
|
||||
`);
|
||||
});
|
||||
|
||||
test("should render dates with 12 hour style", async () => {
|
||||
const wrapper = createLogEventSource({ hourStyle: "12" });
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].emitOpen();
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({
|
||||
data: `2019-06-12T23:55:42.459034602Z <test>foo bar</test>`,
|
||||
});
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
expect(wrapper.find("ul.events")).toMatchInlineSnapshot(`
|
||||
<ul class="events medium">
|
||||
<li><span class="date"><time datetime="2019-06-12T23:55:42.459Z">today at 11:55:42 PM</time></span> <span class="text"><test>foo bar</test></span></li>
|
||||
</ul>
|
||||
`);
|
||||
});
|
||||
|
||||
test("should render dates with 24 hour style", async () => {
|
||||
const wrapper = createLogEventSource({ hourStyle: "24" });
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].emitOpen();
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({
|
||||
data: `2019-06-12T23:55:42.459034602Z <test>foo bar</test>`,
|
||||
});
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
expect(wrapper.find("ul.events")).toMatchInlineSnapshot(`
|
||||
<ul class="events medium">
|
||||
<li><span class="date"><time datetime="2019-06-12T23:55:42.459Z">today at 23:55:42</time></span> <span class="text"><test>foo bar</test></span></li>
|
||||
</ul>
|
||||
`);
|
||||
});
|
||||
|
||||
test("should render messages with filter", async () => {
|
||||
const wrapper = createLogEventSource("test");
|
||||
sources["/api/logs/stream?id=abc"].emitOpen();
|
||||
sources["/api/logs/stream?id=abc"].emitMessage({
|
||||
const wrapper = createLogEventSource({ searchFilter: "test" });
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].emitOpen();
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].emitMessage({
|
||||
data: `2019-06-11T10:55:42.459034602Z Foo bar`,
|
||||
});
|
||||
sources["/api/logs/stream?id=abc"].emitMessage({
|
||||
sources["/api/logs/stream?id=abc&lastEventId="].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>
|
||||
<li><span class="date"><time datetime="2019-06-12T10:55:42.459Z">today at 10:55:42 AM</time></span> <span class="text">This is a <mark>test</mark> <hi></hi></span></li>
|
||||
</ul>
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
import debounce from "lodash.debounce";
|
||||
import InfiniteLoader from "./InfiniteLoader";
|
||||
import config from "../store/config";
|
||||
import containerMixin from "./mixins/container";
|
||||
|
||||
export default {
|
||||
props: ["id"],
|
||||
mixins: [containerMixin],
|
||||
name: "LogEventSource",
|
||||
components: {
|
||||
InfiniteLoader,
|
||||
@@ -20,29 +22,49 @@ export default {
|
||||
return {
|
||||
messages: [],
|
||||
buffer: [],
|
||||
es: null,
|
||||
lastEventId: null,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.es = null;
|
||||
this.loadLogs(this.id);
|
||||
this.flushBuffer = debounce(this.flushNow, 250, { maxWait: 1000 });
|
||||
this.loadLogs();
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.es.close();
|
||||
},
|
||||
methods: {
|
||||
loadLogs(id) {
|
||||
loadLogs() {
|
||||
this.reset();
|
||||
this.es = new EventSource(`${config.base}/api/logs/stream?id=${this.id}`);
|
||||
this.es.addEventListener("container-stopped", (e) => {
|
||||
this.es.close();
|
||||
this.buffer.push({ event: "container-stopped", message: "Container stopped", date: new Date() });
|
||||
this.flushBuffer();
|
||||
this.flushBuffer.flush();
|
||||
});
|
||||
this.connect();
|
||||
},
|
||||
onContainerStopped() {
|
||||
this.es.close();
|
||||
this.buffer.push({ event: "container-stopped", message: "Container stopped", date: new Date(), key: new Date() });
|
||||
this.flushBuffer();
|
||||
this.flushBuffer.flush();
|
||||
},
|
||||
onMessage(e) {
|
||||
this.lastEventId = e.lastEventId;
|
||||
this.buffer.push(this.parseMessage(e.data));
|
||||
this.flushBuffer();
|
||||
},
|
||||
onContainerStateChange(newValue, oldValue) {
|
||||
if (newValue == "running" && newValue != oldValue) {
|
||||
this.buffer.push({
|
||||
event: "container-started",
|
||||
message: "Container started",
|
||||
date: new Date(),
|
||||
key: new Date(),
|
||||
});
|
||||
this.connect();
|
||||
}
|
||||
},
|
||||
connect() {
|
||||
this.es = new EventSource(`${config.base}/api/logs/stream?id=${this.id}&lastEventId=${this.lastEventId ?? ""}`);
|
||||
this.es.addEventListener("container-stopped", (e) => this.onContainerStopped());
|
||||
this.es.addEventListener("error", (e) => console.error("EventSource failed: " + JSON.stringify(e)));
|
||||
this.es.onmessage = (e) => {
|
||||
this.buffer.push(this.parseMessage(e.data));
|
||||
this.flushBuffer();
|
||||
};
|
||||
this.$once("hook:beforeDestroy", () => this.es.close());
|
||||
this.es.onmessage = (e) => this.onMessage(e);
|
||||
},
|
||||
flushNow() {
|
||||
this.messages.push(...this.buffer);
|
||||
@@ -51,11 +73,12 @@ export default {
|
||||
reset() {
|
||||
if (this.es) {
|
||||
this.es.close();
|
||||
this.es = null;
|
||||
this.flushBuffer.cancel();
|
||||
}
|
||||
this.flushBuffer.cancel();
|
||||
this.es = null;
|
||||
this.messages = [];
|
||||
this.buffer = [];
|
||||
this.lastEventId = null;
|
||||
},
|
||||
async loadOlderLogs() {
|
||||
if (this.messages.length < 300) return;
|
||||
@@ -84,14 +107,14 @@ export default {
|
||||
}
|
||||
const key = data.substring(0, i);
|
||||
const date = new Date(key);
|
||||
const message = data.substring(i);
|
||||
const message = data.substring(i + 1);
|
||||
return { key, date, message };
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
id(newValue, oldValue) {
|
||||
if (oldValue !== newValue) {
|
||||
this.loadLogs(newValue);
|
||||
this.loadLogs();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<ul class="events" :class="settings.size">
|
||||
<li v-for="item in filtered" :key="item.key" :class="{ event: !!item.event }">
|
||||
<li v-for="item in filtered" :key="item.key" :data-event="item.event">
|
||||
<span class="date" v-if="settings.showTimestamp"><relative-time :date="item.date"></relative-time></span>
|
||||
<span class="text" v-html="colorize(item.message)"></span>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<script>
|
||||
import { mapActions, mapGetters, mapState } from "vuex";
|
||||
import { mapState } from "vuex";
|
||||
import AnsiConvertor from "ansi-to-html";
|
||||
import DOMPurify from "dompurify";
|
||||
import RelativeTime from "./RelativeTime";
|
||||
@@ -63,8 +63,8 @@ export default {
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.events {
|
||||
padding: 10px;
|
||||
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monaco, monospace;
|
||||
padding: 1em;
|
||||
font-family: SFMono-Regular, Consolas, Liberation Mono, monaco, Menlo, monospace;
|
||||
|
||||
& > li {
|
||||
word-wrap: break-word;
|
||||
@@ -73,6 +73,12 @@ export default {
|
||||
scroll-snap-align: end;
|
||||
scroll-margin-block-end: 5rem;
|
||||
}
|
||||
&[data-event="container-stopped"] {
|
||||
color: #f14668;
|
||||
}
|
||||
&[data-event="container-started"] {
|
||||
color: hsl(141, 53%, 53%);
|
||||
}
|
||||
}
|
||||
|
||||
&.small {
|
||||
@@ -104,10 +110,6 @@ export default {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
li.event {
|
||||
color: #f14668;
|
||||
}
|
||||
|
||||
::v-deep mark {
|
||||
border-radius: 2px;
|
||||
background-color: var(--secondary-color);
|
||||
|
||||
@@ -70,7 +70,7 @@ aside {
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--scheme-main-ter);
|
||||
z-index: 2;
|
||||
z-index: 10;
|
||||
max-height: 100vh;
|
||||
overflow: auto;
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapState } from "vuex";
|
||||
import { mapState } from "vuex";
|
||||
import { formatRelative } from "date-fns";
|
||||
import { enGB, enUS } from "date-fns/locale";
|
||||
import enGB from "date-fns/locale/en-GB";
|
||||
import enUS from "date-fns/locale/en-US";
|
||||
|
||||
const use24Hr =
|
||||
new Intl.DateTimeFormat(undefined, {
|
||||
@@ -30,7 +31,14 @@ export default {
|
||||
computed: {
|
||||
...mapState(["settings"]),
|
||||
locale() {
|
||||
return styles[this.settings.hourStyle];
|
||||
const locale = styles[this.settings.hourStyle];
|
||||
const oldFormatter = locale.formatRelative;
|
||||
return {
|
||||
...locale,
|
||||
formatRelative(token) {
|
||||
return oldFormatter(token) + "p";
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
filters: {
|
||||
|
||||
19
assets/components/mixins/container.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { mapGetters } from "vuex";
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters(["allContainersById"]),
|
||||
container() {
|
||||
return this.allContainersById[this.id];
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
["container.state"](newValue, oldValue) {
|
||||
if (newValue == "running" && newValue != oldValue) {
|
||||
this.onContainerStateChange(newValue, oldValue);
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onContainerStateChange(newValue, oldValue) {},
|
||||
},
|
||||
};
|
||||
@@ -7,7 +7,9 @@
|
||||
<script type="application/json" id="config__json">
|
||||
{
|
||||
"base": "{{ .Base }}",
|
||||
"version": "{{ .Version }}"
|
||||
"version": "{{ .Version }}",
|
||||
"authorizationNeeded": "{{ .AuthorizationNeeded }}",
|
||||
"secured": "{{ .Secured }}"
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
@@ -10,7 +10,7 @@ import Autocomplete from "buefy/dist/esm/autocomplete";
|
||||
import store from "./store";
|
||||
import config from "./store/config";
|
||||
import App from "./App.vue";
|
||||
import { Container, Settings, Index, Show, ContainerNotFound, PageNotFound } from "./pages";
|
||||
import { Container, Settings, Index, Show, ContainerNotFound, PageNotFound, Login } from "./pages";
|
||||
|
||||
Vue.use(VueRouter);
|
||||
Vue.use(Meta);
|
||||
@@ -47,6 +47,11 @@ const routes = [
|
||||
component: Show,
|
||||
name: "show",
|
||||
},
|
||||
{
|
||||
path: "/login",
|
||||
component: Login,
|
||||
name: "login",
|
||||
},
|
||||
{
|
||||
path: "/*",
|
||||
component: PageNotFound,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters, mapState } from "vuex";
|
||||
import { mapGetters } from "vuex";
|
||||
import Search from "../components/Search";
|
||||
import LogContainer from "../components/LogContainer";
|
||||
|
||||
|
||||
@@ -14,5 +14,10 @@
|
||||
<script>
|
||||
export default {
|
||||
name: "ContainerNotFound",
|
||||
metaInfo() {
|
||||
return {
|
||||
title: "Not Found",
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -3,7 +3,14 @@
|
||||
<section class="hero is-small mt-4">
|
||||
<div class="hero-body">
|
||||
<div class="container">
|
||||
<h1 class="title">Hello, there!</h1>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h1 class="title">Hello, there!</h1>
|
||||
</div>
|
||||
<div class="column is-narrow" v-if="secured">
|
||||
<a class="button is-primary is-small" :href="`${base}/logout`">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -28,7 +35,7 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="columns is-centered section">
|
||||
<section class="columns is-centered section is-marginless">
|
||||
<div class="column is-4">
|
||||
<div class="panel">
|
||||
<p class="panel-heading">Containers</p>
|
||||
@@ -84,6 +91,8 @@ export default {
|
||||
version: config.version,
|
||||
search: null,
|
||||
sort: "running",
|
||||
secured: config.secured,
|
||||
base: config.base,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
||||
84
assets/pages/Login.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div class="hero is-halfheight">
|
||||
<div class="hero-body">
|
||||
<div class="container">
|
||||
<section class="columns is-centered section">
|
||||
<div class="column is-4">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<form action="" method="post" @submit.prevent="onLogin" ref="form">
|
||||
<div class="field">
|
||||
<label class="label">Username</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
name="username"
|
||||
autocomplete="username"
|
||||
v-model="username"
|
||||
autofocus
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Password</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
type="password"
|
||||
name="password"
|
||||
autocomplete="current-password"
|
||||
v-model="password"
|
||||
/>
|
||||
</div>
|
||||
<p class="help is-danger" v-if="error">Username and password are not valid.</p>
|
||||
</div>
|
||||
<div class="field is-grouped is-grouped-centered mt-5">
|
||||
<p class="control">
|
||||
<button class="button is-primary" type="submit">Login</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import config from "../store/config";
|
||||
export default {
|
||||
name: "Login",
|
||||
data() {
|
||||
return {
|
||||
username: null,
|
||||
password: null,
|
||||
error: false,
|
||||
};
|
||||
},
|
||||
metaInfo() {
|
||||
return {
|
||||
title: "Authentication Required",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async onLogin() {
|
||||
const response = await fetch(`${config.base}/api/validateCredentials`, {
|
||||
body: new FormData(this.$refs.form),
|
||||
method: "post",
|
||||
});
|
||||
|
||||
if (response.status == 200) {
|
||||
this.error = false;
|
||||
window.location.href = `${config.base}/`;
|
||||
} else {
|
||||
this.error = true;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -14,5 +14,10 @@
|
||||
<script>
|
||||
export default {
|
||||
name: "PageNotFound",
|
||||
metaInfo() {
|
||||
return {
|
||||
title: "404 Error",
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -91,7 +91,6 @@
|
||||
|
||||
<script>
|
||||
import gt from "semver/functions/gt";
|
||||
import valid from "semver/functions/valid";
|
||||
import { mapActions, mapState } from "vuex";
|
||||
import Icon from "../components/Icon";
|
||||
import config from "../store/config";
|
||||
|
||||
@@ -4,3 +4,4 @@ export { default as Show } from "./Show.vue";
|
||||
export { default as Container } from "./Container.vue";
|
||||
export { default as Settings } from "./Settings.vue";
|
||||
export { default as PageNotFound } from "./PageNotFound.vue";
|
||||
export { default as Login } from "./Login.vue";
|
||||
|
||||
@@ -2,8 +2,12 @@ const config = JSON.parse(document.querySelector("script#config__json").textCont
|
||||
if (config.version == "{{ .Version }}") {
|
||||
config.version = "dev";
|
||||
config.base = "";
|
||||
config.authorizationNeeded = false;
|
||||
config.secured = false;
|
||||
} else {
|
||||
config.version = config.version.replace(/^v/, "");
|
||||
config.authorizationNeeded = config.authorizationNeeded === "true";
|
||||
config.secured = config.secured === "true";
|
||||
}
|
||||
|
||||
export default config;
|
||||
|
||||
@@ -16,19 +16,19 @@ const state = {
|
||||
searchFilter: null,
|
||||
isMobile: mql.matches,
|
||||
settings: storage.get(DOZZLE_SETTINGS_KEY),
|
||||
authorizationNeeded: config.authorizationNeeded,
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
SET_CONTAINERS(state, containers) {
|
||||
const containersById = getters.allContainersById({ containers });
|
||||
|
||||
containers.forEach(
|
||||
(container) =>
|
||||
(container.stat =
|
||||
containersById[container.id] && containersById[container.id].stat
|
||||
? containersById[container.id].stat
|
||||
: { memoryUsage: 0, cpu: 0 })
|
||||
);
|
||||
containers.forEach((container) => {
|
||||
container.stat =
|
||||
containersById[container.id] && containersById[container.id].stat
|
||||
? containersById[container.id].stat
|
||||
: { memoryUsage: 0, cpu: 0 };
|
||||
});
|
||||
|
||||
state.containers = containers;
|
||||
},
|
||||
@@ -101,10 +101,12 @@ const getters = {
|
||||
},
|
||||
};
|
||||
|
||||
const es = new EventSource(`${config.base}/api/events/stream`);
|
||||
es.addEventListener("containers-changed", (e) => store.commit("SET_CONTAINERS", JSON.parse(e.data)), false);
|
||||
es.addEventListener("container-stat", (e) => store.dispatch("UPDATE_STATS", JSON.parse(e.data)), false);
|
||||
es.addEventListener("container-die", (e) => store.dispatch("UPDATE_CONTAINER", JSON.parse(e.data)), false);
|
||||
if (!config.authorizationNeeded) {
|
||||
const es = new EventSource(`${config.base}/api/events/stream`);
|
||||
es.addEventListener("containers-changed", (e) => store.commit("SET_CONTAINERS", JSON.parse(e.data)), false);
|
||||
es.addEventListener("container-stat", (e) => store.dispatch("UPDATE_STATS", JSON.parse(e.data)), false);
|
||||
es.addEventListener("container-die", (e) => store.dispatch("UPDATE_CONTAINER", JSON.parse(e.data)), false);
|
||||
}
|
||||
|
||||
mql.addEventListener("change", (e) => store.commit("SET_MOBILE_WIDTH", e.matches));
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@charset "utf-8";
|
||||
@import "~bulma/sass/utilities/initial-variables.sass";
|
||||
|
||||
$body-family: "Roboto", sans-serif;
|
||||
|
||||
$body-background-color: var(--body-background-color);
|
||||
|
||||
$scheme-main: var(--scheme-main);
|
||||
|
||||
@@ -40,16 +40,13 @@ type Client interface {
|
||||
ContainerStats(context.Context, string, chan<- ContainerStat) error
|
||||
}
|
||||
|
||||
// NewClient creates a new instance of Client
|
||||
func NewClient() Client {
|
||||
return NewClientWithFilters(map[string]string{})
|
||||
}
|
||||
|
||||
// NewClientWithFilters creates a new instance of Client with docker filters
|
||||
func NewClientWithFilters(f map[string]string) Client {
|
||||
func NewClientWithFilters(f map[string][]string) Client {
|
||||
filterArgs := filters.NewArgs()
|
||||
for k, v := range f {
|
||||
filterArgs.Add(k, v)
|
||||
for key, values := range f {
|
||||
for _, value := range values {
|
||||
filterArgs.Add(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("filterArgs = %v", filterArgs)
|
||||
@@ -205,9 +202,12 @@ func (d *dockerClient) Events(ctx context.Context) (<-chan ContainerEvent, <-cha
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
messages <- ContainerEvent{
|
||||
ActorID: message.Actor.ID[:12],
|
||||
Name: message.Action,
|
||||
|
||||
if message.Type == "container" {
|
||||
messages <- ContainerEvent{
|
||||
ActorID: message.Actor.ID[:12],
|
||||
Name: message.Action,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
28
go.mod
@@ -1,41 +1,33 @@
|
||||
module github.com/amir20/dozzle
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.4.16 // indirect
|
||||
github.com/Microsoft/go-winio v0.4.18 // indirect
|
||||
github.com/alexflint/go-arg v1.4.2
|
||||
github.com/beme/abide v0.0.0-20190723115211-635a09831760
|
||||
github.com/containerd/containerd v1.4.4 // indirect
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/docker v20.10.5+incompatible
|
||||
github.com/docker/docker v20.10.6+incompatible
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.1 // indirect
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/magiconair/properties v1.8.4
|
||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||
github.com/gorilla/sessions v1.2.1
|
||||
github.com/magiconair/properties v1.8.5
|
||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||
github.com/pelletier/go-toml v1.8.1 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/afero v1.6.0
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.7.1
|
||||
github.com/stretchr/objx v0.3.0 // indirect
|
||||
github.com/stretchr/testify v1.7.0
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 // indirect
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 // indirect
|
||||
golang.org/x/text v0.3.5 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6 // indirect
|
||||
google.golang.org/grpc v1.36.0 // indirect
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
golang.org/x/net v0.0.0-20210420072503-d25e30425868 // indirect
|
||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe // indirect
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210420162539-3c870d7478d2 // indirect
|
||||
google.golang.org/grpc v1.37.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
gotest.tools/v3 v3.0.3 // indirect
|
||||
)
|
||||
|
||||
299
go.sum
@@ -1,55 +1,28 @@
|
||||
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.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/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/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.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
|
||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
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/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
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/Microsoft/go-winio v0.4.18 h1:yjwCO1nhWEShaA5qsmPOBzAOjRCa2PRLsDNZ5yBWXpg=
|
||||
github.com/Microsoft/go-winio v0.4.18/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/alexflint/go-arg v1.4.2 h1:lDWZAXxpAnZUq4qwb86p/3rIJJ2Li81EoMbTMujhVa0=
|
||||
github.com/alexflint/go-arg v1.4.2/go.mod h1:9iRbDxne7LcR/GSvEr7ma++GLpdIU1zrghf2y2768kM=
|
||||
github.com/alexflint/go-scalar v1.0.0 h1:NGupf1XV/Xb04wXskDFzS0KWOLH632W/EO4fAFi+A70=
|
||||
github.com/alexflint/go-scalar v1.0.0/go.mod h1:GpHzbCOZXEKMEcygYQ5n/aa4Aq84zbxjy3MxYW0gjYw=
|
||||
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/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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/containerd/containerd v1.4.4 h1:rtRG4N6Ct7GNssATwgpvMGfnjnwfjnu/Zs9W3Ikzq+M=
|
||||
github.com/containerd/containerd v1.4.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
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/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v20.10.5+incompatible h1:o5WL5onN4awYGwrW7+oTn5x9AF2prw7V0Ox8ZEkoCdg=
|
||||
github.com/docker/docker v20.10.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v20.10.6+incompatible h1:oXI3Vas8TI8Eu/EjH4srKHJBVqraSzJybhxY7Om9faQ=
|
||||
github.com/docker/docker v20.10.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
@@ -59,28 +32,13 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/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/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
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/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
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-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.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
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/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/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
@@ -90,10 +48,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1 h1:jAbXjIeW2ZSW2AwFxlGTDoc2CjI2XujLkV3ArsZFCvc=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
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/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
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/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
@@ -101,141 +57,47 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
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/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
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-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/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/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/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
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/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
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/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY=
|
||||
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
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/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-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/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
|
||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
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/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
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_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
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/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
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/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
|
||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
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.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
|
||||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
@@ -244,162 +106,89 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
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/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
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/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
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-20181029021203-45a5f77698d3/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-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-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
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-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-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/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
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-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-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-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-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-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-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
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/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 h1:b0LrWgu8+q7z4J+0Y3Umo5q1dL7NXBkKBWkaVkAq17E=
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210420072503-d25e30425868 h1:mHVdVrNGft0Bv5N0WIf3/ujpDOQOe6KxvwlIikPbMr0=
|
||||
golang.org/x/net v0.0.0-20210420072503-d25e30425868/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||
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/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-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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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-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-20181116152217-5ac8a444bdc5/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-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-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-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 h1:EZ2mChiOa8udjfp6rRmswTbtZN/QzUQp4ptM4rnjHvc=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe h1:WdX7u8s3yOigWAhHEaDl8r9G+4XwFQEQFtBMYyN+kXQ=
|
||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
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/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
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/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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-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-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-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-20190624222133-a101b041ded4/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/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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.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/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-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-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6 h1:4Xw2NwItrJOFR5s6PnK98PI6Bgw1LhMP1j/rO5WP0S4=
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210420162539-3c870d7478d2 h1:g2sJMUGCpeHZqTx8p3wsAWRS64nFq20i4dvJWcKGqvY=
|
||||
google.golang.org/genproto v0.0.0-20210420162539-3c870d7478d2/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||
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.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c=
|
||||
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -408,27 +197,15 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -436,8 +213,4 @@ gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
|
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||
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.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
|
||||
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 81 KiB |
@@ -68,7 +68,7 @@ describe("home page", () => {
|
||||
await page.click("aside ul.menu-list li a");
|
||||
|
||||
await page.waitForSelector("ul.events li span.text");
|
||||
const text = await page.$eval("ul.events li:nth-child(2) span.text", (e) => e.textContent);
|
||||
const text = await page.$eval("ul.events li:nth-child(1) span.text", (e) => e.textContent);
|
||||
|
||||
expect(text).toContain("Dozzle version dev");
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ services:
|
||||
environment:
|
||||
- DOZZLE_FILTER=name=custom_base
|
||||
- DOZZLE_BASE=/foobarbase
|
||||
- DOZZLE_NO_ANALYTICS=1
|
||||
build:
|
||||
context: ..
|
||||
dozzle:
|
||||
@@ -15,6 +16,7 @@ services:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
environment:
|
||||
- DOZZLE_FILTER=name=dozzle
|
||||
- DOZZLE_NO_ANALYTICS=1
|
||||
build:
|
||||
context: ..
|
||||
integration:
|
||||
@@ -26,6 +28,7 @@ services:
|
||||
environment:
|
||||
- DEFAULT_URL=http://dozzle:8080/
|
||||
- CUSTOM_URL=http://custom_base:8080/foobarbase
|
||||
- DOZZLE_NO_ANALYTICS=1
|
||||
depends_on:
|
||||
- dozzle
|
||||
- custom_base
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"dependencies": {
|
||||
"jest": "^26.0.1",
|
||||
"jest-image-snapshot": "^4.0.0",
|
||||
"puppeteer": "^8.0.0"
|
||||
"puppeteer": "^9.0.0"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "jest-puppeteer",
|
||||
@@ -19,6 +19,6 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest-puppeteer": "^4.4.0"
|
||||
"jest-puppeteer": "^5.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.8.3"
|
||||
|
||||
"@babel/code-frame@^7.12.13":
|
||||
version "7.12.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658"
|
||||
integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.12.13"
|
||||
|
||||
"@babel/core@^7.1.0", "@babel/core@^7.7.5":
|
||||
version "7.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e"
|
||||
@@ -126,6 +133,11 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.8.3"
|
||||
|
||||
"@babel/helper-validator-identifier@^7.14.0":
|
||||
version "7.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288"
|
||||
integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.9.0":
|
||||
version "7.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz#ad53562a7fc29b3b9a91bbf7d10397fd146346ed"
|
||||
@@ -145,6 +157,15 @@
|
||||
"@babel/traverse" "^7.9.0"
|
||||
"@babel/types" "^7.9.0"
|
||||
|
||||
"@babel/highlight@^7.12.13":
|
||||
version "7.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf"
|
||||
integrity sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.14.0"
|
||||
chalk "^2.0.0"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/highlight@^7.8.3":
|
||||
version "7.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079"
|
||||
@@ -298,37 +319,17 @@
|
||||
exec-sh "^0.3.2"
|
||||
minimist "^1.2.0"
|
||||
|
||||
"@hapi/address@2.x.x":
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
|
||||
integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==
|
||||
"@hapi/hoek@^9.0.0":
|
||||
version "9.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131"
|
||||
integrity sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug==
|
||||
|
||||
"@hapi/bourne@1.x.x":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-1.3.2.tgz#0a7095adea067243ce3283e1b56b8a8f453b242a"
|
||||
integrity sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==
|
||||
|
||||
"@hapi/hoek@8.x.x", "@hapi/hoek@^8.3.0":
|
||||
version "8.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06"
|
||||
integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==
|
||||
|
||||
"@hapi/joi@^15.0.3":
|
||||
version "15.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-15.1.1.tgz#c675b8a71296f02833f8d6d243b34c57b8ce19d7"
|
||||
integrity sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==
|
||||
"@hapi/topo@^5.0.0":
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.0.0.tgz#c19af8577fa393a06e9c77b60995af959be721e7"
|
||||
integrity sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw==
|
||||
dependencies:
|
||||
"@hapi/address" "2.x.x"
|
||||
"@hapi/bourne" "1.x.x"
|
||||
"@hapi/hoek" "8.x.x"
|
||||
"@hapi/topo" "3.x.x"
|
||||
|
||||
"@hapi/topo@3.x.x":
|
||||
version "3.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.6.tgz#68d935fa3eae7fdd5ab0d7f953f3205d8b2bfc29"
|
||||
integrity sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^8.3.0"
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
|
||||
"@istanbuljs/load-nyc-config@^1.0.0":
|
||||
version "1.0.0"
|
||||
@@ -401,6 +402,16 @@
|
||||
"@types/node" "*"
|
||||
jest-mock "^26.6.2"
|
||||
|
||||
"@jest/environment@^27.0.1":
|
||||
version "27.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.0.1.tgz#27ed89bf8179c0a030690f063d922d6da7a519ac"
|
||||
integrity sha512-nG+r3uSs2pOTsdhgt6lUm4ZGJLRcTc6HZIkrFsVpPcdSqEpJehEny9r9y2Bmhkn8fKXWdGCYJKF3i4nKO0HSmA==
|
||||
dependencies:
|
||||
"@jest/fake-timers" "^27.0.1"
|
||||
"@jest/types" "^27.0.1"
|
||||
"@types/node" "*"
|
||||
jest-mock "^27.0.1"
|
||||
|
||||
"@jest/fake-timers@^26.6.2":
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad"
|
||||
@@ -413,6 +424,18 @@
|
||||
jest-mock "^26.6.2"
|
||||
jest-util "^26.6.2"
|
||||
|
||||
"@jest/fake-timers@^27.0.1":
|
||||
version "27.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.0.1.tgz#6987a596b0bcf8c07653086076c17058b4c77b5c"
|
||||
integrity sha512-3CyLJQnHzKI4TCJSCo+I9TzIHjSK4RrNEk93jFM6Q9+9WlSJ3mpMq/p2YuKMe0SiHKbmZOd5G/Ll5ofF9Xkw9g==
|
||||
dependencies:
|
||||
"@jest/types" "^27.0.1"
|
||||
"@sinonjs/fake-timers" "^7.0.2"
|
||||
"@types/node" "*"
|
||||
jest-message-util "^27.0.1"
|
||||
jest-mock "^27.0.1"
|
||||
jest-util "^27.0.1"
|
||||
|
||||
"@jest/globals@^26.6.2":
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.6.2.tgz#5b613b78a1aa2655ae908eba638cc96a20df720a"
|
||||
@@ -516,6 +539,34 @@
|
||||
"@types/yargs" "^15.0.0"
|
||||
chalk "^4.0.0"
|
||||
|
||||
"@jest/types@^27.0.1":
|
||||
version "27.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.0.1.tgz#631738c942e70045ebbf42a3f9b433036d3845e4"
|
||||
integrity sha512-8A25RRV4twZutsx2D+7WphnDsp7If9Yu6ko0Gxwrwv8BiWESFzka34+Aa2kC8w9xewt7SDuCUSZ6IiAFVj3PRg==
|
||||
dependencies:
|
||||
"@types/istanbul-lib-coverage" "^2.0.0"
|
||||
"@types/istanbul-reports" "^3.0.0"
|
||||
"@types/node" "*"
|
||||
"@types/yargs" "^16.0.0"
|
||||
chalk "^4.0.0"
|
||||
|
||||
"@sideway/address@^4.1.0":
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.1.tgz#9e321e74310963fdf8eebfbee09c7bd69972de4d"
|
||||
integrity sha512-+I5aaQr3m0OAmMr7RQ3fR9zx55sejEYR2BFJaxL+zT3VM2611X0SHvPWIbAUBZVTn/YzYKbV8gJ2oT/QELknfQ==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
|
||||
"@sideway/formula@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c"
|
||||
integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==
|
||||
|
||||
"@sideway/pinpoint@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
|
||||
integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
|
||||
|
||||
"@sinonjs/commons@^1.7.0":
|
||||
version "1.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.1.tgz#da5fd19a5f71177a53778073978873964f49acf1"
|
||||
@@ -530,6 +581,13 @@
|
||||
dependencies:
|
||||
"@sinonjs/commons" "^1.7.0"
|
||||
|
||||
"@sinonjs/fake-timers@^7.0.2":
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.0.tgz#8f13af27d842cbf51ad4502e05562fe9391d084e"
|
||||
integrity sha512-hAEzXi6Wbvlb67NnGMGSNOeAflLVnMa4yliPU/ty1qjgW/vAletH15/v/esJwASSIA0YlIyjnloenFbEZc9q9A==
|
||||
dependencies:
|
||||
"@sinonjs/commons" "^1.7.0"
|
||||
|
||||
"@types/babel__core@^7.0.0":
|
||||
version "7.1.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.9.tgz#77e59d438522a6fb898fa43dc3455c6e72f3963d"
|
||||
@@ -644,6 +702,13 @@
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@types/yargs@^16.0.0":
|
||||
version "16.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.3.tgz#4b6d35bb8e680510a7dc2308518a80ee1ef27e01"
|
||||
integrity sha512-YlFfTGS+zqCgXuXNV26rOIeETOkXnGQXP/pjjL9P0gO/EP9jTmc7pUBhx+jVEIxpq41RX33GQ7N3DzOSfZoglQ==
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@types/yauzl@^2.9.1":
|
||||
version "2.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.1.tgz#d10f69f9f522eef3cf98e30afb684a1e1ec923af"
|
||||
@@ -728,6 +793,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
|
||||
"@types/color-name" "^1.1.1"
|
||||
color-convert "^2.0.1"
|
||||
|
||||
ansi-styles@^5.0.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
|
||||
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
|
||||
|
||||
anymatch@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
|
||||
@@ -808,6 +878,13 @@ aws4@^1.8.0:
|
||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
|
||||
integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
|
||||
|
||||
axios@^0.21.1:
|
||||
version "0.21.1"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
|
||||
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
|
||||
dependencies:
|
||||
follow-redirects "^1.10.0"
|
||||
|
||||
babel-jest@^26.6.3:
|
||||
version "26.6.3"
|
||||
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056"
|
||||
@@ -1022,7 +1099,7 @@ chalk@^1.1.3:
|
||||
strip-ansi "^3.0.0"
|
||||
supports-color "^2.0.0"
|
||||
|
||||
chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2:
|
||||
chalk@^2.0.0, chalk@^2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||
@@ -1031,18 +1108,10 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2:
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chalk@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
|
||||
integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chalk@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.0.0.tgz#6e98081ed2d17faab615eb52ac66ec1fe6209e72"
|
||||
integrity sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==
|
||||
chalk@^4.0.0, chalk@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
|
||||
integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
@@ -1062,6 +1131,11 @@ ci-info@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
|
||||
integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
|
||||
|
||||
ci-info@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.1.1.tgz#9a32fcefdf7bcdb6f0a7e1c0f8098ec57897b80a"
|
||||
integrity sha512-kdRWLBIJwdsYJWYJFtAFFYxybguqeF91qpZaggjG5Nf8QKdizFG2hjqvaTXbxFIcYbSaD74KpAXv6BSm17DHEQ==
|
||||
|
||||
cjs-module-lexer@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f"
|
||||
@@ -1146,16 +1220,16 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
commander@^2.11.0:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||
|
||||
commander@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e"
|
||||
integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==
|
||||
|
||||
commander@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
|
||||
integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==
|
||||
|
||||
component-emitter@^1.2.1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
|
||||
@@ -1178,11 +1252,6 @@ copy-descriptor@^0.1.0:
|
||||
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
|
||||
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
|
||||
|
||||
core-js@^2.6.5:
|
||||
version "2.6.11"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
|
||||
integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
|
||||
|
||||
core-util-is@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
@@ -1256,7 +1325,7 @@ debug@4, debug@^4.1.0, debug@^4.1.1:
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debug@^2.2.0, debug@^2.3.3, debug@^2.6.8:
|
||||
debug@^2.2.0, debug@^2.3.3:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
@@ -1320,10 +1389,10 @@ detect-newline@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
|
||||
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
|
||||
|
||||
devtools-protocol@0.0.854822:
|
||||
version "0.0.854822"
|
||||
resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.854822.tgz#eac3a5260a6b3b4e729a09fdc0c77b0d322e777b"
|
||||
integrity sha512-xd4D8kHQtB0KtWW0c9xBZD5LVtm9chkMOfs/3Yn01RhT/sFIsVtzTtypfKoFfWBaL+7xCYLxjOLkhwPXaX/Kcg==
|
||||
devtools-protocol@0.0.869402:
|
||||
version "0.0.869402"
|
||||
resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.869402.tgz#03ade701761742e43ae4de5dc188bcd80f156d8d"
|
||||
integrity sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA==
|
||||
|
||||
diff-sequences@^26.6.2:
|
||||
version "26.6.2"
|
||||
@@ -1464,10 +1533,10 @@ expand-tilde@^1.2.2:
|
||||
dependencies:
|
||||
os-homedir "^1.0.1"
|
||||
|
||||
expect-puppeteer@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/expect-puppeteer/-/expect-puppeteer-4.4.0.tgz#1c948af08acdd6c8cbdb7f90e617f44d86888886"
|
||||
integrity sha512-6Ey4Xy2xvmuQu7z7YQtMsaMV0EHJRpVxIDOd5GRrm04/I3nkTKIutELfECsLp6le+b3SSa3cXhPiw6PgqzxYWA==
|
||||
expect-puppeteer@^5.0.4:
|
||||
version "5.0.4"
|
||||
resolved "https://registry.yarnpkg.com/expect-puppeteer/-/expect-puppeteer-5.0.4.tgz#54bfdecabb2acb3e3f0d0292cd3dab2dd8ff5a81"
|
||||
integrity sha512-NV7jSiKhK+byocxg9A+0av+Q2RSCP9bcLVRz7zhHaESeCOkuomMvl9oD+uo1K+NdqRCXhNkQlUGWlmtbrpR1qw==
|
||||
|
||||
expect@^26.6.2:
|
||||
version "26.6.2"
|
||||
@@ -1597,14 +1666,14 @@ find-pkg@^0.1.2:
|
||||
dependencies:
|
||||
find-file-up "^0.1.2"
|
||||
|
||||
find-process@^1.4.3:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.yarnpkg.com/find-process/-/find-process-1.4.3.tgz#25f9105dc32e42abad4636752c37c51cd57dce45"
|
||||
integrity sha512-+IA+AUsQCf3uucawyTwMWcY+2M3FXq3BRvw3S+j5Jvydjk31f/+NPWpYZOJs+JUs2GvxH4Yfr6Wham0ZtRLlPA==
|
||||
find-process@^1.4.4:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/find-process/-/find-process-1.4.4.tgz#52820561162fda0d1feef9aed5d56b3787f0fd6e"
|
||||
integrity sha512-rRSuT1LE4b+BFK588D2V8/VG9liW0Ark1XJgroxZXI0LtwmQJOb490DvDYvbm+Hek9ETFzTutGfJ90gumITPhQ==
|
||||
dependencies:
|
||||
chalk "^2.0.1"
|
||||
commander "^2.11.0"
|
||||
debug "^2.6.8"
|
||||
chalk "^4.0.0"
|
||||
commander "^5.1.0"
|
||||
debug "^4.1.1"
|
||||
|
||||
find-up@^4.0.0, find-up@^4.1.0:
|
||||
version "4.1.0"
|
||||
@@ -1614,6 +1683,11 @@ find-up@^4.0.0, find-up@^4.1.0:
|
||||
locate-path "^5.0.0"
|
||||
path-exists "^4.0.0"
|
||||
|
||||
follow-redirects@^1.10.0:
|
||||
version "1.13.3"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267"
|
||||
integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==
|
||||
|
||||
for-in@^0.1.3:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1"
|
||||
@@ -1844,9 +1918,9 @@ homedir-polyfill@^1.0.0:
|
||||
parse-passwd "^1.0.0"
|
||||
|
||||
hosted-git-info@^2.1.4:
|
||||
version "2.8.8"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
|
||||
integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
|
||||
version "2.8.9"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
|
||||
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
|
||||
|
||||
html-encoding-sniffer@^2.0.1:
|
||||
version "2.0.1"
|
||||
@@ -1961,6 +2035,13 @@ is-ci@^2.0.0:
|
||||
dependencies:
|
||||
ci-info "^2.0.0"
|
||||
|
||||
is-ci@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.0.tgz#c7e7be3c9d8eef7d0fa144390bd1e4b88dc4c994"
|
||||
integrity sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ==
|
||||
dependencies:
|
||||
ci-info "^3.1.1"
|
||||
|
||||
is-core-module@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.0.0.tgz#58531b70aed1db7c0e8d4eb1a0a2d1ddd64bd12d"
|
||||
@@ -2216,18 +2297,18 @@ jest-config@^26.6.3:
|
||||
micromatch "^4.0.2"
|
||||
pretty-format "^26.6.2"
|
||||
|
||||
jest-dev-server@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-dev-server/-/jest-dev-server-4.4.0.tgz#557113faae2877452162696aa94c1e44491ab011"
|
||||
integrity sha512-STEHJ3iPSC8HbrQ3TME0ozGX2KT28lbT4XopPxUm2WimsX3fcB3YOptRh12YphQisMhfqNSNTZUmWyT3HEXS2A==
|
||||
jest-dev-server@^5.0.3:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/jest-dev-server/-/jest-dev-server-5.0.3.tgz#324bf6426477450ec3dae349ee9223d43f8be368"
|
||||
integrity sha512-aJR3a5KdY18Lsz+VbREKwx2HM3iukiui+J9rlv9o6iYTwZCSsJazSTStcD9K1q0AIF3oA+FqLOKDyo/sc7+fJw==
|
||||
dependencies:
|
||||
chalk "^3.0.0"
|
||||
chalk "^4.1.1"
|
||||
cwd "^0.10.0"
|
||||
find-process "^1.4.3"
|
||||
prompts "^2.3.0"
|
||||
spawnd "^4.4.0"
|
||||
find-process "^1.4.4"
|
||||
prompts "^2.4.1"
|
||||
spawnd "^5.0.0"
|
||||
tree-kill "^1.2.2"
|
||||
wait-on "^3.3.0"
|
||||
wait-on "^5.3.0"
|
||||
|
||||
jest-diff@^26.6.2:
|
||||
version "26.6.2"
|
||||
@@ -2282,15 +2363,28 @@ jest-environment-node@^26.6.2:
|
||||
jest-mock "^26.6.2"
|
||||
jest-util "^26.6.2"
|
||||
|
||||
jest-environment-puppeteer@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-environment-puppeteer/-/jest-environment-puppeteer-4.4.0.tgz#d82a37e0e0c51b63cc6b15dea101d53967508860"
|
||||
integrity sha512-iV8S8+6qkdTM6OBR/M9gKywEk8GDSOe05hspCs5D8qKSwtmlUfdtHfB4cakdc68lC6YfK3AUsLirpfgodCHjzQ==
|
||||
jest-environment-node@^27.0.1:
|
||||
version "27.0.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.0.1.tgz#7d7df7ae191477a823ffb4fcc0772b4c23ec5c87"
|
||||
integrity sha512-/p94lo0hx+hbKUw1opnRFUPPsjncRBEUU+2Dh7BuxX8Nr4rRiTivLYgXzo79FhaeMYV0uiV5WAbHBq6xC11JJg==
|
||||
dependencies:
|
||||
chalk "^3.0.0"
|
||||
"@jest/environment" "^27.0.1"
|
||||
"@jest/fake-timers" "^27.0.1"
|
||||
"@jest/types" "^27.0.1"
|
||||
"@types/node" "*"
|
||||
jest-mock "^27.0.1"
|
||||
jest-util "^27.0.1"
|
||||
|
||||
jest-environment-puppeteer@^5.0.4:
|
||||
version "5.0.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-environment-puppeteer/-/jest-environment-puppeteer-5.0.4.tgz#ed64689bf200923828ca98761b4da36eb8ce31bc"
|
||||
integrity sha512-wd4EDOD4QRi11QZ1IV8WsL1wlnnMUtcqtU0BNm+REzRtg78K2XHn3jS6YxGeXIOnsgrJeHxsD7DlRZ/GkFteLg==
|
||||
dependencies:
|
||||
chalk "^4.1.1"
|
||||
cwd "^0.10.0"
|
||||
jest-dev-server "^4.4.0"
|
||||
merge-deep "^3.0.2"
|
||||
jest-dev-server "^5.0.3"
|
||||
jest-environment-node "^27.0.1"
|
||||
merge-deep "^3.0.3"
|
||||
|
||||
jest-get-type@^26.3.0:
|
||||
version "26.3.0"
|
||||
@@ -2319,9 +2413,9 @@ jest-haste-map@^26.6.2:
|
||||
fsevents "^2.1.2"
|
||||
|
||||
jest-image-snapshot@^4.0.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-image-snapshot/-/jest-image-snapshot-4.4.0.tgz#7c5282cc62280086b6cbcda203d50b6ee0b0e71e"
|
||||
integrity sha512-lGlHxoEHLYL0qyr6E7SA04/eMT/PFWxHRNmhEJZpw80iE1XH/wSHEKiK4oDGJI5FQ4hhWPXmcH5tDlTvsAHogQ==
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-image-snapshot/-/jest-image-snapshot-4.5.0.tgz#77d3a4c37b61eb88cd6ecacef8cecf676cb2ec68"
|
||||
integrity sha512-9Q1xyjyUsepNgn6/DaMnT4maaCSi3yaDp/xq1bnsOTk/tR3utygOTLOFOwztNrrkWX7HIXcm5PcHC2Mc5iBwUw==
|
||||
dependencies:
|
||||
chalk "^1.1.3"
|
||||
get-stdin "^5.0.1"
|
||||
@@ -2390,6 +2484,21 @@ jest-message-util@^26.6.2:
|
||||
slash "^3.0.0"
|
||||
stack-utils "^2.0.2"
|
||||
|
||||
jest-message-util@^27.0.1:
|
||||
version "27.0.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.0.1.tgz#382b7c55d8e0b1aba9eeb41d3cfdd34e451210ed"
|
||||
integrity sha512-w8BfON2GwWORkos8BsxcwwQrLkV2s1ENxSRXK43+6yuquDE2hVxES/jrFqOArpP1ETVqqMmktU6iGkG8ncVzeA==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.12.13"
|
||||
"@jest/types" "^27.0.1"
|
||||
"@types/stack-utils" "^2.0.0"
|
||||
chalk "^4.0.0"
|
||||
graceful-fs "^4.2.4"
|
||||
micromatch "^4.0.4"
|
||||
pretty-format "^27.0.1"
|
||||
slash "^3.0.0"
|
||||
stack-utils "^2.0.3"
|
||||
|
||||
jest-mock@^26.6.2:
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302"
|
||||
@@ -2398,18 +2507,26 @@ jest-mock@^26.6.2:
|
||||
"@jest/types" "^26.6.2"
|
||||
"@types/node" "*"
|
||||
|
||||
jest-mock@^27.0.1:
|
||||
version "27.0.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.0.1.tgz#8394e297bc3dfed980961622cb51fd042b4acf5a"
|
||||
integrity sha512-fXCSZQDT5hUcAUy8OBnB018x7JFOMQnz4XfpSKEbfpWzL6o5qaLRhgf2Qg2NPuVKmC/fgOf33Edj8wjF4I24CQ==
|
||||
dependencies:
|
||||
"@jest/types" "^27.0.1"
|
||||
"@types/node" "*"
|
||||
|
||||
jest-pnp-resolver@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c"
|
||||
integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==
|
||||
|
||||
jest-puppeteer@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-puppeteer/-/jest-puppeteer-4.4.0.tgz#4b906e638a5e3782ed865e7b673c82047b85952e"
|
||||
integrity sha512-ZaiCTlPZ07B9HW0erAWNX6cyzBqbXMM7d2ugai4epBDKpKvRDpItlRQC6XjERoJELKZsPziFGS0OhhUvTvQAXA==
|
||||
jest-puppeteer@^5.0.1:
|
||||
version "5.0.4"
|
||||
resolved "https://registry.yarnpkg.com/jest-puppeteer/-/jest-puppeteer-5.0.4.tgz#c52e3379c11425ce974d025c1a8bf9f599da4b3f"
|
||||
integrity sha512-IUOVKgHEaKsLqahZy/J/DvXB59SQx4AVpZKTRDvJzCdkvdGc3NVsNwUhovr6SK+HOK1TOiqAiXPTAPiIq3mkrg==
|
||||
dependencies:
|
||||
expect-puppeteer "^4.4.0"
|
||||
jest-environment-puppeteer "^4.4.0"
|
||||
expect-puppeteer "^5.0.4"
|
||||
jest-environment-puppeteer "^5.0.4"
|
||||
|
||||
jest-regex-util@^26.0.0:
|
||||
version "26.0.0"
|
||||
@@ -2540,6 +2657,18 @@ jest-util@^26.6.2:
|
||||
is-ci "^2.0.0"
|
||||
micromatch "^4.0.2"
|
||||
|
||||
jest-util@^27.0.1:
|
||||
version "27.0.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.0.1.tgz#324ed9879d129c1e64f9169a739d6d50d7928769"
|
||||
integrity sha512-lEw3waSmEOO4ZkwkUlFSvg4es1+8+LIkSGxp/kF60K0+vMR3Dv3O2HMZhcln9NHqSQzpVbsDT6OeMzUPW7DfRg==
|
||||
dependencies:
|
||||
"@jest/types" "^27.0.1"
|
||||
"@types/node" "*"
|
||||
chalk "^4.0.0"
|
||||
graceful-fs "^4.2.4"
|
||||
is-ci "^3.0.0"
|
||||
picomatch "^2.2.3"
|
||||
|
||||
jest-validate@^26.6.2:
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec"
|
||||
@@ -2583,6 +2712,17 @@ jest@^26.0.1:
|
||||
import-local "^3.0.2"
|
||||
jest-cli "^26.6.3"
|
||||
|
||||
joi@^17.3.0:
|
||||
version "17.4.0"
|
||||
resolved "https://registry.yarnpkg.com/joi/-/joi-17.4.0.tgz#b5c2277c8519e016316e49ababd41a1908d9ef20"
|
||||
integrity sha512-F4WiW2xaV6wc1jxete70Rw4V/VuMd6IN+a5ilZsxG4uYtUXWu2kq9W5P2dz30e7Gmw8RCbY/u/uk+dMPma9tAg==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
"@hapi/topo" "^5.0.0"
|
||||
"@sideway/address" "^4.1.0"
|
||||
"@sideway/formula" "^3.0.0"
|
||||
"@sideway/pinpoint" "^2.0.0"
|
||||
|
||||
js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@@ -2751,10 +2891,10 @@ lodash.sortby@^4.7.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
|
||||
|
||||
lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.4:
|
||||
version "4.17.19"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
|
||||
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
|
||||
lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
||||
lru-cache@^6.0.0:
|
||||
version "6.0.0"
|
||||
@@ -2789,10 +2929,10 @@ map-visit@^1.0.0:
|
||||
dependencies:
|
||||
object-visit "^1.0.0"
|
||||
|
||||
merge-deep@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/merge-deep/-/merge-deep-3.0.2.tgz#f39fa100a4f1bd34ff29f7d2bf4508fbb8d83ad2"
|
||||
integrity sha512-T7qC8kg4Zoti1cFd8Cr0M+qaZfOwjlPDEdZIIPPB2JZctjaPM4fX+i7HOId69tAti2fvO6X5ldfYUONDODsrkA==
|
||||
merge-deep@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/merge-deep/-/merge-deep-3.0.3.tgz#1a2b2ae926da8b2ae93a0ac15d90cd1922766003"
|
||||
integrity sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA==
|
||||
dependencies:
|
||||
arr-union "^3.1.0"
|
||||
clone-deep "^0.2.4"
|
||||
@@ -2830,6 +2970,14 @@ micromatch@^4.0.2:
|
||||
braces "^3.0.1"
|
||||
picomatch "^2.0.5"
|
||||
|
||||
micromatch@^4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9"
|
||||
integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==
|
||||
dependencies:
|
||||
braces "^3.0.1"
|
||||
picomatch "^2.2.3"
|
||||
|
||||
mime-db@1.43.0:
|
||||
version "1.43.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58"
|
||||
@@ -3145,6 +3293,11 @@ picomatch@^2.0.4, picomatch@^2.0.5:
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
||||
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
|
||||
|
||||
picomatch@^2.2.3:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
|
||||
integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
|
||||
|
||||
pirates@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87"
|
||||
@@ -3191,12 +3344,22 @@ pretty-format@^26.6.2:
|
||||
ansi-styles "^4.0.0"
|
||||
react-is "^17.0.1"
|
||||
|
||||
pretty-format@^27.0.1:
|
||||
version "27.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.0.1.tgz#c4094621dfbd3e8ab751964d1cf01edc6f88474d"
|
||||
integrity sha512-qE+0J6c/gd+R6XTcQgPJMc5hMJNsxzSF5p8iZSbMZ7GQzYGlSLNkh2P80Wa2dbF4gEVUsJEgcrBY+1L2/j265w==
|
||||
dependencies:
|
||||
"@jest/types" "^27.0.1"
|
||||
ansi-regex "^5.0.0"
|
||||
ansi-styles "^5.0.0"
|
||||
react-is "^17.0.1"
|
||||
|
||||
progress@^2.0.1:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
||||
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
||||
|
||||
prompts@^2.0.1, prompts@^2.3.0:
|
||||
prompts@^2.0.1:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.2.tgz#480572d89ecf39566d2bd3fe2c9fccb7c4c0b068"
|
||||
integrity sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA==
|
||||
@@ -3204,6 +3367,14 @@ prompts@^2.0.1, prompts@^2.3.0:
|
||||
kleur "^3.0.3"
|
||||
sisteransi "^1.0.4"
|
||||
|
||||
prompts@^2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61"
|
||||
integrity sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==
|
||||
dependencies:
|
||||
kleur "^3.0.3"
|
||||
sisteransi "^1.0.5"
|
||||
|
||||
proxy-from-env@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||
@@ -3227,13 +3398,13 @@ punycode@^2.1.0, punycode@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||
|
||||
puppeteer@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-8.0.0.tgz#a236669118aa795331c2d0ca19877159e7664705"
|
||||
integrity sha512-D0RzSWlepeWkxPPdK3xhTcefj8rjah1791GE82Pdjsri49sy11ci/JQsAO8K2NRukqvwEtcI+ImP5F4ZiMvtIQ==
|
||||
puppeteer@^9.0.0:
|
||||
version "9.1.1"
|
||||
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-9.1.1.tgz#f74b7facf86887efd6c6b9fabb7baae6fdce012c"
|
||||
integrity sha512-W+nOulP2tYd/ZG99WuZC/I5ljjQQ7EUw/jQGcIb9eu8mDlZxNY2SgcJXTLG9h5gRvqA3uJOe4hZXYsd3EqioMw==
|
||||
dependencies:
|
||||
debug "^4.1.0"
|
||||
devtools-protocol "0.0.854822"
|
||||
devtools-protocol "0.0.869402"
|
||||
extract-zip "^2.0.0"
|
||||
https-proxy-agent "^5.0.0"
|
||||
node-fetch "^2.6.1"
|
||||
@@ -3322,7 +3493,7 @@ request-promise-native@^1.0.8:
|
||||
stealthy-require "^1.1.1"
|
||||
tough-cookie "^2.3.3"
|
||||
|
||||
request@^2.88.0, request@^2.88.2:
|
||||
request@^2.88.2:
|
||||
version "2.88.2"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
|
||||
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
|
||||
@@ -3429,10 +3600,12 @@ rsvp@^4.8.4:
|
||||
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
|
||||
integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==
|
||||
|
||||
rx@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
|
||||
integrity sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=
|
||||
rxjs@^6.6.3:
|
||||
version "6.6.7"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"
|
||||
integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2:
|
||||
version "5.2.0"
|
||||
@@ -3554,12 +3727,12 @@ shellwords@^0.1.1:
|
||||
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
|
||||
integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
|
||||
|
||||
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||
signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
||||
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
|
||||
|
||||
sisteransi@^1.0.4:
|
||||
sisteransi@^1.0.4, sisteransi@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
|
||||
integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
|
||||
@@ -3638,15 +3811,15 @@ source-map@^0.7.3:
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
||||
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
||||
|
||||
spawnd@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/spawnd/-/spawnd-4.4.0.tgz#bb52c5b34a22e3225ae1d3acb873b2cd58af0886"
|
||||
integrity sha512-jLPOfB6QOEgMOQY15Z6+lwZEhH3F5ncXxIaZ7WHPIapwNNLyjrs61okj3VJ3K6tmP5TZ6cO0VAu9rEY4MD4YQg==
|
||||
spawnd@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/spawnd/-/spawnd-5.0.0.tgz#ea72200bdc468998e84e1c3e7b914ce85fc1c32c"
|
||||
integrity sha512-28+AJr82moMVWolQvlAIv3JcYDkjkFTEmfDc503wxrF5l2rQ3dFz6DpbXp3kD4zmgGGldfM4xM4v1sFj/ZaIOA==
|
||||
dependencies:
|
||||
exit "^0.1.2"
|
||||
signal-exit "^3.0.2"
|
||||
signal-exit "^3.0.3"
|
||||
tree-kill "^1.2.2"
|
||||
wait-port "^0.2.7"
|
||||
wait-port "^0.2.9"
|
||||
|
||||
spdx-correct@^3.0.0:
|
||||
version "3.1.1"
|
||||
@@ -3713,6 +3886,13 @@ stack-utils@^2.0.2:
|
||||
dependencies:
|
||||
escape-string-regexp "^2.0.0"
|
||||
|
||||
stack-utils@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277"
|
||||
integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==
|
||||
dependencies:
|
||||
escape-string-regexp "^2.0.0"
|
||||
|
||||
static-extend@^0.1.1:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
|
||||
@@ -3930,6 +4110,11 @@ tree-kill@^1.2.2:
|
||||
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
|
||||
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
|
||||
|
||||
tslib@^1.9.0:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
tunnel-agent@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
|
||||
@@ -4074,21 +4259,21 @@ w3c-xmlserializer@^2.0.0:
|
||||
dependencies:
|
||||
xml-name-validator "^3.0.0"
|
||||
|
||||
wait-on@^3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-3.3.0.tgz#9940981d047a72a9544a97b8b5fca45b2170a082"
|
||||
integrity sha512-97dEuUapx4+Y12aknWZn7D25kkjMk16PbWoYzpSdA8bYpVfS6hpl2a2pOWZ3c+Tyt3/i4/pglyZctG3J4V1hWQ==
|
||||
wait-on@^5.3.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-5.3.0.tgz#584e17d4b3fe7b46ac2b9f8e5e102c005c2776c7"
|
||||
integrity sha512-DwrHrnTK+/0QFaB9a8Ol5Lna3k7WvUR4jzSKmz0YaPBpuN2sACyiPVKVfj6ejnjcajAcvn3wlbTyMIn9AZouOg==
|
||||
dependencies:
|
||||
"@hapi/joi" "^15.0.3"
|
||||
core-js "^2.6.5"
|
||||
minimist "^1.2.0"
|
||||
request "^2.88.0"
|
||||
rx "^4.1.0"
|
||||
axios "^0.21.1"
|
||||
joi "^17.3.0"
|
||||
lodash "^4.17.21"
|
||||
minimist "^1.2.5"
|
||||
rxjs "^6.6.3"
|
||||
|
||||
wait-port@^0.2.7:
|
||||
version "0.2.7"
|
||||
resolved "https://registry.yarnpkg.com/wait-port/-/wait-port-0.2.7.tgz#cdb4b78e662328099b187c7bb75fe0aa9cb6eb6c"
|
||||
integrity sha512-pJ6cSBIa0w1sDg4y/wXN4bmvhM9OneOvwdFHo647L2NShBi/oXG4lRaLic5cO1HaYGbUhEvratPfl/WMlIC+tg==
|
||||
wait-port@^0.2.9:
|
||||
version "0.2.9"
|
||||
resolved "https://registry.yarnpkg.com/wait-port/-/wait-port-0.2.9.tgz#3905cf271b5dbe37a85c03b85b418b81cb24ee55"
|
||||
integrity sha512-hQ/cVKsNqGZ/UbZB/oakOGFqic00YAMM5/PEj3Bt4vKarv2jWIWzDbqlwT94qMs/exAQAsvMOq99sZblV92zxQ==
|
||||
dependencies:
|
||||
chalk "^2.4.2"
|
||||
commander "^3.0.2"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
module.exports = {
|
||||
clearMocks: true,
|
||||
testEnvironment: "jsdom",
|
||||
moduleFileExtensions: ["js", "json", "vue"],
|
||||
coveragePathIgnorePatterns: ["node_modules"],
|
||||
testPathIgnorePatterns: ["node_modules", "<rootDir>/integration/"],
|
||||
|
||||
140
main.go
@@ -4,90 +4,95 @@ import (
|
||||
"context"
|
||||
"embed"
|
||||
"io/fs"
|
||||
"net/url"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/alexflint/go-arg"
|
||||
"github.com/amir20/dozzle/analytics"
|
||||
"github.com/amir20/dozzle/docker"
|
||||
"github.com/amir20/dozzle/web"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var (
|
||||
addr = ""
|
||||
base = ""
|
||||
level = ""
|
||||
tailSize = 300
|
||||
filters map[string]string
|
||||
version = "dev"
|
||||
version = "dev"
|
||||
)
|
||||
|
||||
type args struct {
|
||||
Addr string `arg:"env:DOZZLE_ADDR" default:":8080"`
|
||||
Base string `arg:"env:DOZZLE_BASE" default:"/"`
|
||||
Level string `arg:"env:DOZZLE_LEVEL" default:"info"`
|
||||
TailSize int `arg:"env:DOZZLE_TAILSIZE" default:"300"`
|
||||
Key string `arg:"env:DOZZLE_KEY"`
|
||||
Username string `arg:"env:DOZZLE_USERNAME"`
|
||||
Password string `arg:"env:DOZZLE_PASSWORD"`
|
||||
NoAnalytics bool `arg:"--no-analytics,env:DOZZLE_NO_ANALYTICS"`
|
||||
FilterStrings []string `arg:"env:DOZZLE_FILTER,--filter,separate"`
|
||||
Filter map[string][]string `arg:"-"`
|
||||
}
|
||||
|
||||
func (args) Version() string {
|
||||
return version
|
||||
}
|
||||
|
||||
//go:embed static
|
||||
var content embed.FS
|
||||
|
||||
type handler struct {
|
||||
client docker.Client
|
||||
}
|
||||
func main() {
|
||||
var args args
|
||||
parser := arg.MustParse(&args)
|
||||
args.Filter = make(map[string][]string)
|
||||
|
||||
func init() {
|
||||
pflag.String("addr", ":8080", "http service address")
|
||||
pflag.String("base", "/", "base address of the application to mount")
|
||||
pflag.String("level", "info", "logging level")
|
||||
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.Parse()
|
||||
|
||||
viper.AutomaticEnv()
|
||||
viper.SetEnvPrefix("DOZZLE")
|
||||
viper.BindPFlags(pflag.CommandLine)
|
||||
|
||||
addr = viper.GetString("addr")
|
||||
base = viper.GetString("base")
|
||||
level = viper.GetString("level")
|
||||
tailSize = viper.GetInt("tailSize")
|
||||
|
||||
// Until https://github.com/spf13/viper/issues/911 is fixed. We have to use this hacky way.
|
||||
// filters = viper.GetStringMapString("filter")
|
||||
if value, ok := os.LookupEnv("DOZZLE_FILTER"); ok {
|
||||
log.Infof("Parsing %s", value)
|
||||
urlValues, err := url.ParseQuery(strings.ReplaceAll(value, ",", "&"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
filters = map[string]string{}
|
||||
for k, v := range urlValues {
|
||||
filters[k] = v[0]
|
||||
for _, filter := range args.FilterStrings {
|
||||
pos := strings.Index(filter, "=")
|
||||
if pos == -1 {
|
||||
parser.Fail("each filter should be of the form key=value")
|
||||
}
|
||||
key := filter[:pos]
|
||||
val := filter[pos+1:]
|
||||
args.Filter[key] = append(args.Filter[key], val)
|
||||
}
|
||||
|
||||
l, _ := log.ParseLevel(level)
|
||||
log.SetLevel(l)
|
||||
level, _ := log.ParseLevel(args.Level)
|
||||
log.SetLevel(level)
|
||||
|
||||
log.SetFormatter(&log.TextFormatter{
|
||||
DisableTimestamp: true,
|
||||
DisableLevelTruncation: true,
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.Infof("Dozzle version %s", version)
|
||||
dockerClient := docker.NewClientWithFilters(filters)
|
||||
dockerClient := docker.NewClientWithFilters(args.Filter)
|
||||
_, err := dockerClient.ListContainers()
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Could not connect to Docker Engine: %v", err)
|
||||
}
|
||||
|
||||
if args.Username != "" || args.Password != "" {
|
||||
if args.Username == "" || args.Password == "" {
|
||||
log.Fatalf("Username AND password are required for authentication")
|
||||
}
|
||||
|
||||
if args.Key == "" {
|
||||
log.Fatalf("Key is required for authentication")
|
||||
}
|
||||
}
|
||||
|
||||
config := web.Config{
|
||||
Addr: addr,
|
||||
Base: base,
|
||||
Addr: args.Addr,
|
||||
Base: args.Base,
|
||||
Version: version,
|
||||
TailSize: tailSize,
|
||||
TailSize: args.TailSize,
|
||||
Key: args.Key,
|
||||
Username: args.Username,
|
||||
Password: args.Password,
|
||||
}
|
||||
|
||||
static, err := fs.Sub(content, "static")
|
||||
@@ -95,22 +100,53 @@ func main() {
|
||||
log.Fatalf("Could not open embedded static folder: %v", err)
|
||||
}
|
||||
|
||||
srv := web.CreateServer(dockerClient, static, config)
|
||||
if _, ok := os.LookupEnv("LIVE_FS"); ok {
|
||||
log.Info("Using live filesystem at ./static")
|
||||
static = os.DirFS("./static")
|
||||
}
|
||||
|
||||
srv := web.CreateServer(dockerClient, static, config)
|
||||
go doStartEvent(args)
|
||||
go func() {
|
||||
log.Infof("Accepting connections on %s", srv.Addr)
|
||||
if err := srv.ListenAndServe(); err != nil {
|
||||
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
signal.Notify(c, os.Kill)
|
||||
signal.Notify(c, syscall.SIGTERM)
|
||||
<-c
|
||||
log.Infof("Shutting down...")
|
||||
log.Info("Shutting down...")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
srv.Shutdown(ctx)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func doStartEvent(arg args) {
|
||||
if arg.NoAnalytics {
|
||||
log.Debug("Analytics disabled.")
|
||||
return
|
||||
}
|
||||
host, err := os.Hostname()
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
|
||||
event := analytics.StartEvent{
|
||||
ClientId: host,
|
||||
Version: version,
|
||||
FilterLength: len(arg.Filter),
|
||||
CustomAddress: arg.Addr != ":8080",
|
||||
CustomBase: arg.Base != "/",
|
||||
TailSize: arg.TailSize,
|
||||
Protected: arg.Username != "",
|
||||
}
|
||||
|
||||
if err := analytics.SendStartEvent(event); err != nil {
|
||||
log.Debug(err)
|
||||
}
|
||||
}
|
||||
|
||||
53
package.json
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "dozzle",
|
||||
"version": "3.4.6",
|
||||
"version": "3.7.0",
|
||||
"description": "Realtime log viewer for docker containers. ",
|
||||
"scripts": {
|
||||
"watch": "npm-run-all -p watch:*",
|
||||
"watch:assets": "webpack --mode=development --watch",
|
||||
"watch:server": "reflex -c .reflex",
|
||||
"watch:server": "LIVE_FS=true reflex -c .reflex",
|
||||
"predev": "make fake_static",
|
||||
"dev": "npm-run-all -p dev-server watch:server",
|
||||
"dev-server": "webpack serve --mode=development",
|
||||
@@ -27,13 +27,13 @@
|
||||
},
|
||||
"homepage": "https://github.com/amir20/dozzle#readme",
|
||||
"dependencies": {
|
||||
"ansi-to-html": "^0.6.14",
|
||||
"buefy": "^0.9.5",
|
||||
"ansi-to-html": "^0.6.15",
|
||||
"buefy": "^0.9.7",
|
||||
"bulma": "^0.9.2",
|
||||
"date-fns": "^2.19.0",
|
||||
"dompurify": "^2.2.7",
|
||||
"date-fns": "^2.21.3",
|
||||
"dompurify": "^2.2.8",
|
||||
"fuzzysort": "^1.1.4",
|
||||
"hotkeys-js": "^3.8.3",
|
||||
"hotkeys-js": "^3.8.5",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"semver": "^7.3.5",
|
||||
@@ -45,37 +45,37 @@
|
||||
"vuex": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.13.14",
|
||||
"@babel/plugin-transform-runtime": "^7.13.10",
|
||||
"@babel/core": "^7.14.3",
|
||||
"@babel/plugin-transform-runtime": "^7.14.3",
|
||||
"@vue/component-compiler-utils": "^3.2.0",
|
||||
"@vue/test-utils": "^1.1.3",
|
||||
"@vue/test-utils": "^1.2.0",
|
||||
"autoprefixer": "^10.2.5",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"babel-jest": "^26.6.3",
|
||||
"babel-jest": "^27.0.1",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"caniuse-lite": "^1.0.30001205",
|
||||
"css-loader": "^5.2.0",
|
||||
"caniuse-lite": "^1.0.30001230",
|
||||
"css-loader": "^5.2.6",
|
||||
"eventsourcemock": "^2.0.0",
|
||||
"html-webpack-plugin": "^5.3.1",
|
||||
"husky": "^6.0.0",
|
||||
"jest": "^26.6.3",
|
||||
"jest": "^27.0.1",
|
||||
"jest-serializer-vue": "^2.0.2",
|
||||
"lint-staged": "^10.5.4",
|
||||
"mini-css-extract-plugin": "^1.4.0",
|
||||
"lint-staged": "^11.0.0",
|
||||
"mini-css-extract-plugin": "^1.6.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.2.9",
|
||||
"postcss-loader": "^5.2.0",
|
||||
"prettier": "^2.2.1",
|
||||
"release-it": "^14.5.0",
|
||||
"sass": "^1.32.8",
|
||||
"sass-loader": "^11.0.1",
|
||||
"postcss": "^8.3.0",
|
||||
"postcss-loader": "^5.3.0",
|
||||
"prettier": "^2.3.0",
|
||||
"release-it": "^14.7.0",
|
||||
"sass": "^1.34.0",
|
||||
"sass-loader": "^11.1.1",
|
||||
"vue-hot-reload-api": "^2.3.4",
|
||||
"vue-jest": "^3.0.7",
|
||||
"vue-loader": "^15.9.6",
|
||||
"vue-loader": "^15.9.7",
|
||||
"vue-style-loader": "^4.1.3",
|
||||
"vue-template-compiler": "^2.6.12",
|
||||
"webpack": "^5.30.0",
|
||||
"webpack-cli": "^4.6.0",
|
||||
"webpack": "^5.37.1",
|
||||
"webpack-cli": "^4.7.0",
|
||||
"webpack-dev-server": "^3.11.2",
|
||||
"webpack-pwa-manifest": "^4.3.0"
|
||||
},
|
||||
@@ -86,7 +86,8 @@
|
||||
},
|
||||
"release-it": {
|
||||
"github": {
|
||||
"release": false
|
||||
"release": false,
|
||||
"releaseNotes": "git log --pretty=format:\"* %s (%h)\" $(git describe --abbrev=0 --tags $(git rev-list --tags --skip=1 --max-count=1))...HEAD~1"
|
||||
},
|
||||
"npm": {
|
||||
"publish": false
|
||||
|
||||
@@ -23,6 +23,45 @@ Location: /foobar/
|
||||
|
||||
<a href="/foobar/">Moved Permanently</a>.
|
||||
|
||||
/* snapshot: Test_createRoutes_redirect_with_auth */
|
||||
HTTP/1.1 307 Temporary Redirect
|
||||
Connection: close
|
||||
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self'; manifest-src 'self'; connect-src 'self' api.github.com; require-trusted-types-for 'script'
|
||||
Content-Type: text/html; charset=utf-8
|
||||
Location: /foobar/login
|
||||
|
||||
<a href="/foobar/login">Temporary Redirect</a>.
|
||||
|
||||
/* snapshot: Test_createRoutes_username_password */
|
||||
HTTP/1.1 307 Temporary Redirect
|
||||
Connection: close
|
||||
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self'; manifest-src 'self'; connect-src 'self' api.github.com; require-trusted-types-for 'script'
|
||||
Content-Type: text/html; charset=utf-8
|
||||
Location: /login
|
||||
|
||||
<a href="/login">Temporary Redirect</a>.
|
||||
|
||||
/* snapshot: Test_createRoutes_username_password_invalid */
|
||||
HTTP/1.1 401 Unauthorized
|
||||
Connection: close
|
||||
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self'; manifest-src 'self'; connect-src 'self' api.github.com; require-trusted-types-for 'script'
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
X-Content-Type-Options: nosniff
|
||||
|
||||
Unauthorized
|
||||
|
||||
/* snapshot: Test_createRoutes_username_password_valid_session */
|
||||
HTTP/1.1 200 OK
|
||||
Connection: close
|
||||
Cache-Control: no-cache
|
||||
Connection: keep-alive
|
||||
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self'; manifest-src 'self'; connect-src 'self' api.github.com; require-trusted-types-for 'script'
|
||||
Content-Type: text/event-stream
|
||||
X-Accel-Buffering: no
|
||||
|
||||
event: container-stopped
|
||||
data: end of stream
|
||||
|
||||
/* snapshot: Test_createRoutes_version */
|
||||
HTTP/1.1 200 OK
|
||||
Connection: close
|
||||
|
||||
117
web/auth.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/sessions"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var secured = false
|
||||
var store *sessions.CookieStore
|
||||
|
||||
const authorityKey = "AUTH_TIMESTAMP"
|
||||
const sessionName = "session"
|
||||
|
||||
func initializeAuth(h *handler) {
|
||||
secured = false
|
||||
if h.config.Username != "" && h.config.Password != "" {
|
||||
store = sessions.NewCookieStore([]byte(h.config.Key))
|
||||
store.Options.HttpOnly = true
|
||||
store.Options.SameSite = http.SameSiteLaxMode
|
||||
store.Options.MaxAge = 0
|
||||
secured = true
|
||||
}
|
||||
}
|
||||
|
||||
func authorizationRequired(f http.HandlerFunc) http.Handler {
|
||||
if secured {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if isAuthorized(r) {
|
||||
f(w, r)
|
||||
} else {
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return http.HandlerFunc(f)
|
||||
}
|
||||
}
|
||||
|
||||
func isAuthorized(r *http.Request) bool {
|
||||
if !secured {
|
||||
return true
|
||||
}
|
||||
|
||||
session, _ := store.Get(r, sessionName)
|
||||
if session.IsNew {
|
||||
return false
|
||||
}
|
||||
|
||||
if _, ok := session.Values[authorityKey]; ok {
|
||||
// TODO check for timestamp
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *handler) isAuthorizationNeeded(r *http.Request) bool {
|
||||
return secured && !isAuthorized(r)
|
||||
}
|
||||
|
||||
func (h *handler) validateCredentials(w http.ResponseWriter, r *http.Request) {
|
||||
if !secured {
|
||||
log.Panic("Validating credentials without username and password should not happen")
|
||||
}
|
||||
|
||||
if r.Method != "POST" {
|
||||
log.Fatal("Expecting credential validation method to be POST")
|
||||
http.Error(w, http.StatusText(http.StatusNotAcceptable), http.StatusNotAcceptable)
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.ParseMultipartForm(4 * 1024); err != nil {
|
||||
log.Fatalf("Error while parsing form data: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
user := r.PostFormValue("username")
|
||||
pass := r.PostFormValue("password")
|
||||
session, _ := store.Get(r, sessionName)
|
||||
|
||||
if user == h.config.Username && pass == h.config.Password {
|
||||
session.Values[authorityKey] = time.Now().Unix()
|
||||
|
||||
if err := session.Save(r, w); err != nil {
|
||||
log.Fatalf("Error while parsing saving session: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(http.StatusText(http.StatusOK)))
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
func (h *handler) clearSession(w http.ResponseWriter, r *http.Request) {
|
||||
if !secured {
|
||||
log.Panic("Validating credentials with secured=false should not happen")
|
||||
}
|
||||
|
||||
session, _ := store.Get(r, sessionName)
|
||||
delete(session.Values, authorityKey)
|
||||
|
||||
if err := session.Save(r, w); err != nil {
|
||||
log.Fatalf("Error while parsing saving session: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, r, h.config.Base, http.StatusTemporaryRedirect)
|
||||
}
|
||||
12
web/csp.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func cspHeaders(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'; img-src 'self'; manifest-src 'self'; connect-src 'self' api.github.com; require-trusted-types-for 'script'")
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
110
web/events.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/amir20/dozzle/docker"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
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()
|
||||
|
||||
events, err := h.client.Events(ctx)
|
||||
stats := make(chan docker.ContainerStat)
|
||||
|
||||
if containers, err := h.client.ListContainers(); err == nil {
|
||||
for _, c := range containers {
|
||||
if c.State == "running" {
|
||||
if err := h.client.ContainerStats(ctx, c.ID, stats); err != nil {
|
||||
log.Errorf("error while streaming container stats: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := sendContainersJSON(h.client, w); err != nil {
|
||||
log.Errorf("error while encoding containers to stream: %v", err)
|
||||
}
|
||||
|
||||
f.Flush()
|
||||
|
||||
for {
|
||||
select {
|
||||
case stat := <-stats:
|
||||
bytes, _ := json.Marshal(stat)
|
||||
if _, err := fmt.Fprintf(w, "event: container-stat\ndata: %s\n\n", string(bytes)); err != nil {
|
||||
log.Errorf("error writing stat to event stream: %v", err)
|
||||
return
|
||||
}
|
||||
f.Flush()
|
||||
case event, ok := <-events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
switch event.Name {
|
||||
case "start", "die":
|
||||
log.Debugf("triggering docker event: %v", event.Name)
|
||||
if event.Name == "start" {
|
||||
log.Debugf("found new container with id: %v", event.ActorID)
|
||||
if err := h.client.ContainerStats(ctx, event.ActorID, stats); err != nil {
|
||||
log.Errorf("error when streaming new container stats: %v", err)
|
||||
}
|
||||
if err := sendContainersJSON(h.client, w); err != nil {
|
||||
log.Errorf("error encoding containers to stream: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
bytes, _ := json.Marshal(event)
|
||||
if _, err := fmt.Fprintf(w, "event: container-%s\ndata: %s\n\n", event.Name, string(bytes)); err != nil {
|
||||
log.Errorf("error writing event to event stream: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
f.Flush()
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-err:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sendContainersJSON(client docker.Client, w http.ResponseWriter) error {
|
||||
containers, err := client.ListContainers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprint(w, "event: containers-changed\ndata: "); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(w).Encode(containers); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprint(w, "\n\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
148
web/logs.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
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")
|
||||
|
||||
reader, err := h.client.ContainerLogsBetweenDates(r.Context(), id, from, to)
|
||||
defer reader.Close()
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
io.Copy(w, reader)
|
||||
}
|
||||
|
||||
func (h *handler) downloadLogs(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.URL.Query().Get("id")
|
||||
container, err := h.client.FindContainer(id)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
from := time.Unix(container.Created, 0)
|
||||
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%v.log.gz", container.ID))
|
||||
w.Header().Set("Content-Type", "application/gzip")
|
||||
zw := gzip.NewWriter(w)
|
||||
defer zw.Close()
|
||||
zw.Name = fmt.Sprintf("%v.log", container.ID)
|
||||
zw.Comment = "Logs generated by Dozzle"
|
||||
zw.ModTime = now
|
||||
|
||||
reader, err := h.client.ContainerLogsBetweenDates(r.Context(), container.ID, from, now)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
io.Copy(zw, reader)
|
||||
}
|
||||
|
||||
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, err := h.client.FindContainer(id)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
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")
|
||||
|
||||
lastEventId := r.Header.Get("Last-Event-ID")
|
||||
if len(r.URL.Query().Get("lastEventId")) > 0 {
|
||||
lastEventId = r.URL.Query().Get("lastEventId")
|
||||
}
|
||||
|
||||
reader, err := h.client.ContainerLogs(r.Context(), container.ID, h.config.TailSize, lastEventId)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
fmt.Fprintf(w, "event: container-stopped\ndata: end of stream\n\n")
|
||||
f.Flush()
|
||||
} else {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
buffered := bufio.NewReader(reader)
|
||||
var readerError error
|
||||
var message string
|
||||
for {
|
||||
message, readerError = buffered.ReadString('\n')
|
||||
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()
|
||||
if readerError != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("streaming stopped: %v", container.ID)
|
||||
|
||||
if readerError == io.EOF {
|
||||
log.Debugf("container stopped: %v", container.ID)
|
||||
fmt.Fprintf(w, "event: container-stopped\ndata: end of stream\n\n")
|
||||
f.Flush()
|
||||
} else if readerError != context.Canceled {
|
||||
log.Errorf("unknown error while streaming %v", readerError.Error())
|
||||
}
|
||||
|
||||
log.WithField("routines", runtime.NumGoroutine()).Debug("runtime goroutine stats")
|
||||
|
||||
if log.IsLevelEnabled(log.DebugLevel) {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
// For info on each, see: https://golang.org/pkg/runtime/#MemStats
|
||||
log.WithFields(log.Fields{
|
||||
"allocated": humanize.Bytes(m.Alloc),
|
||||
"totalAllocated": humanize.Bytes(m.TotalAlloc),
|
||||
"system": humanize.Bytes(m.Sys),
|
||||
}).Debug("runtime mem stats")
|
||||
}
|
||||
}
|
||||
334
web/routes.go
@@ -1,25 +1,14 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"time"
|
||||
"path"
|
||||
|
||||
"github.com/amir20/dozzle/docker"
|
||||
"github.com/dustin/go-humanize"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -31,13 +20,15 @@ type Config struct {
|
||||
Addr string
|
||||
Version string
|
||||
TailSize int
|
||||
Key string
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
client docker.Client
|
||||
content fs.FS
|
||||
config *Config
|
||||
fileServer http.Handler
|
||||
client docker.Client
|
||||
content fs.FS
|
||||
config *Config
|
||||
}
|
||||
|
||||
// CreateServer creates a service for http handler
|
||||
@@ -50,21 +41,27 @@ func CreateServer(c docker.Client, content fs.FS, config Config) *http.Server {
|
||||
return &http.Server{Addr: config.Addr, Handler: createRouter(handler)}
|
||||
}
|
||||
|
||||
var fileServer http.Handler
|
||||
|
||||
func createRouter(h *handler) *mux.Router {
|
||||
initializeAuth(h)
|
||||
|
||||
base := h.config.Base
|
||||
r := mux.NewRouter()
|
||||
r.Use(setCSPHeaders)
|
||||
r.Use(cspHeaders)
|
||||
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/logs/stream", h.streamLogs)
|
||||
s.HandleFunc("/api/logs/download", h.downloadLogs)
|
||||
s.HandleFunc("/api/logs", h.fetchLogsBetweenDates)
|
||||
s.HandleFunc("/api/events/stream", h.streamEvents)
|
||||
s.HandleFunc("/version", h.version)
|
||||
s.Handle("/api/logs/stream", authorizationRequired(h.streamLogs))
|
||||
s.Handle("/api/logs/download", authorizationRequired(h.downloadLogs))
|
||||
s.Handle("/api/logs", authorizationRequired(h.fetchLogsBetweenDates))
|
||||
s.Handle("/api/events/stream", authorizationRequired(h.streamEvents))
|
||||
s.HandleFunc("/api/validateCredentials", h.validateCredentials)
|
||||
s.Handle("/logout", authorizationRequired(h.clearSession))
|
||||
s.Handle("/version", authorizationRequired(h.version))
|
||||
|
||||
if log.IsLevelEnabled(log.DebugLevel) {
|
||||
s.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux)
|
||||
@@ -76,270 +73,61 @@ func createRouter(h *handler) *mux.Router {
|
||||
s.PathPrefix("/").Handler(http.StripPrefix(base, http.HandlerFunc(h.index)))
|
||||
}
|
||||
|
||||
h.fileServer = http.FileServer(http.FS(h.content))
|
||||
fileServer = http.FileServer(http.FS(h.content))
|
||||
|
||||
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'; img-src 'self'; manifest-src 'self'; 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) {
|
||||
_, err := h.content.Open(req.URL.Path)
|
||||
if err == nil && req.URL.Path != "" && req.URL.Path != "/" {
|
||||
h.fileServer.ServeHTTP(w, req)
|
||||
fileServer.ServeHTTP(w, req)
|
||||
} else {
|
||||
file, err := h.content.Open("index.html")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bytes, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tmpl, err := template.New("index.html").Parse(string(bytes))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
path := ""
|
||||
if h.config.Base != "/" {
|
||||
path = h.config.Base
|
||||
}
|
||||
|
||||
data := struct {
|
||||
Base string
|
||||
Version string
|
||||
}{path, h.config.Version}
|
||||
err = tmpl.Execute(w, data)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
|
||||
reader, err := h.client.ContainerLogsBetweenDates(r.Context(), id, from, to)
|
||||
defer reader.Close()
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
io.Copy(w, reader)
|
||||
}
|
||||
|
||||
func (h *handler) downloadLogs(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.URL.Query().Get("id")
|
||||
container, err := h.client.FindContainer(id)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
from := time.Unix(container.Created, 0)
|
||||
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%v.log.gz", container.ID))
|
||||
w.Header().Set("Content-Type", "application/gzip")
|
||||
zw := gzip.NewWriter(w)
|
||||
defer zw.Close()
|
||||
zw.Name = fmt.Sprintf("%v.log", container.ID)
|
||||
zw.Comment = "Logs generated by Dozzle"
|
||||
zw.ModTime = now
|
||||
|
||||
reader, err := h.client.ContainerLogsBetweenDates(r.Context(), container.ID, from, now)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
io.Copy(zw, reader)
|
||||
}
|
||||
|
||||
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, err := h.client.FindContainer(id)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
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")
|
||||
|
||||
reader, err := h.client.ContainerLogs(r.Context(), container.ID, h.config.TailSize, r.Header.Get("Last-Event-ID"))
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
fmt.Fprintf(w, "event: container-stopped\ndata: end of stream\n\n")
|
||||
f.Flush()
|
||||
} else {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
scanner := bufio.NewScanner(reader)
|
||||
for scanner.Scan() {
|
||||
message := scanner.Text()
|
||||
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()
|
||||
}
|
||||
|
||||
log.Debugf("streaming stopped: %v", container.ID)
|
||||
|
||||
if scanner.Err() == nil {
|
||||
log.Debugf("container stopped: %v", container.ID)
|
||||
fmt.Fprintf(w, "event: container-stopped\ndata: end of stream\n\n")
|
||||
f.Flush()
|
||||
} else if scanner.Err() != context.Canceled {
|
||||
log.Errorf("unknown error while streaming %v", scanner.Err())
|
||||
}
|
||||
|
||||
log.WithField("routines", runtime.NumGoroutine()).Debug("runtime goroutine stats")
|
||||
|
||||
if log.IsLevelEnabled(log.DebugLevel) {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
// For info on each, see: https://golang.org/pkg/runtime/#MemStats
|
||||
log.WithFields(log.Fields{
|
||||
"allocated": humanize.Bytes(m.Alloc),
|
||||
"totalAllocated": humanize.Bytes(m.TotalAlloc),
|
||||
"system": humanize.Bytes(m.Sys),
|
||||
}).Debug("runtime mem 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()
|
||||
|
||||
events, err := h.client.Events(ctx)
|
||||
stats := make(chan docker.ContainerStat)
|
||||
|
||||
if containers, err := h.client.ListContainers(); err == nil {
|
||||
for _, c := range containers {
|
||||
if c.State == "running" {
|
||||
if err := h.client.ContainerStats(ctx, c.ID, stats); err != nil {
|
||||
log.Errorf("error while streaming container stats: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := sendContainersJSON(h.client, w); err != nil {
|
||||
log.Errorf("error while encoding containers to stream: %v", err)
|
||||
}
|
||||
|
||||
f.Flush()
|
||||
|
||||
for {
|
||||
select {
|
||||
case stat := <-stats:
|
||||
bytes, _ := json.Marshal(stat)
|
||||
if _, err := fmt.Fprintf(w, "event: container-stat\ndata: %s\n\n", string(bytes)); err != nil {
|
||||
log.Errorf("error writing stat to event stream: %v", err)
|
||||
return
|
||||
}
|
||||
f.Flush()
|
||||
case event, ok := <-events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
switch event.Name {
|
||||
case "start", "die":
|
||||
log.Debugf("triggering docker event: %v", event.Name)
|
||||
if event.Name == "start" {
|
||||
log.Debugf("found new container with id: %v", event.ActorID)
|
||||
if err := h.client.ContainerStats(ctx, event.ActorID, stats); err != nil {
|
||||
log.Errorf("error when streaming new container stats: %v", err)
|
||||
}
|
||||
if err := sendContainersJSON(h.client, w); err != nil {
|
||||
log.Errorf("error encoding containers to stream: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
bytes, _ := json.Marshal(event)
|
||||
if _, err := fmt.Fprintf(w, "event: container-%s\ndata: %s\n\n", event.Name, string(bytes)); err != nil {
|
||||
log.Errorf("error writing event to event stream: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
f.Flush()
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-err:
|
||||
if !isAuthorized(req) && req.URL.Path != "login" {
|
||||
http.Redirect(w, req, path.Clean(h.config.Base+"/login"), http.StatusTemporaryRedirect)
|
||||
return
|
||||
}
|
||||
h.executeTemplate(w, req)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) executeTemplate(w http.ResponseWriter, req *http.Request) {
|
||||
file, err := h.content.Open("index.html")
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
bytes, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
tmpl, err := template.New("index.html").Parse(string(bytes))
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
path := ""
|
||||
if h.config.Base != "/" {
|
||||
path = h.config.Base
|
||||
}
|
||||
|
||||
data := struct {
|
||||
Base string
|
||||
Version string
|
||||
AuthorizationNeeded bool
|
||||
Secured bool
|
||||
}{
|
||||
path,
|
||||
h.config.Version,
|
||||
h.isAuthorizationNeeded(req),
|
||||
secured,
|
||||
}
|
||||
err = tmpl.Execute(w, data)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) version(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "%v", h.config.Version)
|
||||
}
|
||||
|
||||
func sendContainersJSON(client docker.Client, w http.ResponseWriter) error {
|
||||
containers, err := client.ListContainers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprint(w, "event: containers-changed\ndata: "); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(w).Encode(containers); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprint(w, "\n\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/magiconair/properties/assert"
|
||||
|
||||
"github.com/amir20/dozzle/docker"
|
||||
@@ -238,14 +243,9 @@ func Test_handler_streamEvents_error_request(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_createRoutes_index(t *testing.T) {
|
||||
mockedClient := new(MockedClient)
|
||||
fs := afero.NewMemMapFs()
|
||||
require.NoError(t, afero.WriteFile(fs, "index.html", []byte("index page"), 0644), "WriteFile should have no error.")
|
||||
handler := createRouter(&handler{
|
||||
client: mockedClient,
|
||||
content: afero.NewIOFS(fs),
|
||||
config: &Config{Base: "/"},
|
||||
})
|
||||
handler := createHandler(nil, afero.NewIOFS(fs), Config{Base: "/"})
|
||||
req, err := http.NewRequest("GET", "/", nil)
|
||||
require.NoError(t, err, "NewRequest should not return an error.")
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -255,15 +255,10 @@ func Test_createRoutes_index(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_createRoutes_redirect(t *testing.T) {
|
||||
mockedClient := new(MockedClient)
|
||||
fs := afero.NewMemMapFs()
|
||||
require.NoError(t, afero.WriteFile(fs, "index.html", []byte("index page"), 0644), "WriteFile should have no error.")
|
||||
|
||||
handler := createRouter(&handler{
|
||||
client: mockedClient,
|
||||
content: afero.NewIOFS(fs),
|
||||
config: &Config{Base: "/foobar"},
|
||||
})
|
||||
handler := createHandler(nil, afero.NewIOFS(fs), Config{Base: "/foobar"})
|
||||
req, err := http.NewRequest("GET", "/foobar", nil)
|
||||
require.NoError(t, err, "NewRequest should not return an error.")
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -272,16 +267,23 @@ func Test_createRoutes_redirect(t *testing.T) {
|
||||
abide.AssertHTTPResponse(t, t.Name(), rr.Result())
|
||||
}
|
||||
|
||||
func Test_createRoutes_redirect_with_auth(t *testing.T) {
|
||||
fs := afero.NewMemMapFs()
|
||||
require.NoError(t, afero.WriteFile(fs, "index.html", []byte("index page"), 0644), "WriteFile should have no error.")
|
||||
|
||||
handler := createHandler(nil, afero.NewIOFS(fs), Config{Base: "/foobar", Username: "amir", Password: "password", Key: "key"})
|
||||
req, err := http.NewRequest("GET", "/foobar/", nil)
|
||||
require.NoError(t, err, "NewRequest should not return an error.")
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(rr, req)
|
||||
abide.AssertHTTPResponse(t, t.Name(), rr.Result())
|
||||
}
|
||||
|
||||
func Test_createRoutes_foobar(t *testing.T) {
|
||||
mockedClient := new(MockedClient)
|
||||
fs := afero.NewMemMapFs()
|
||||
require.NoError(t, afero.WriteFile(fs, "index.html", []byte("foo page"), 0644), "WriteFile should have no error.")
|
||||
|
||||
handler := createRouter(&handler{
|
||||
client: mockedClient,
|
||||
content: afero.NewIOFS(fs),
|
||||
config: &Config{Base: "/foobar"},
|
||||
})
|
||||
handler := createHandler(nil, afero.NewIOFS(fs), Config{Base: "/foobar"})
|
||||
req, err := http.NewRequest("GET", "/foobar/", nil)
|
||||
require.NoError(t, err, "NewRequest should not return an error.")
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -291,17 +293,11 @@ func Test_createRoutes_foobar(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_createRoutes_foobar_file(t *testing.T) {
|
||||
mockedClient := new(MockedClient)
|
||||
fs := afero.NewMemMapFs()
|
||||
require.NoError(t, afero.WriteFile(fs, "index.html", []byte("index page"), 0644), "WriteFile should have no error.")
|
||||
require.NoError(t, afero.WriteFile(fs, "test", []byte("test page"), 0644), "WriteFile should have no error.")
|
||||
|
||||
handler := createRouter(&handler{
|
||||
client: mockedClient,
|
||||
content: afero.NewIOFS(fs),
|
||||
config: &Config{Base: "/foobar"},
|
||||
fileServer: nil,
|
||||
})
|
||||
handler := createHandler(nil, afero.NewIOFS(fs), Config{Base: "/foobar"})
|
||||
req, err := http.NewRequest("GET", "/foobar/test", nil)
|
||||
require.NoError(t, err, "NewRequest should not return an error.")
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -311,15 +307,9 @@ func Test_createRoutes_foobar_file(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_createRoutes_version(t *testing.T) {
|
||||
mockedClient := new(MockedClient)
|
||||
fs := afero.NewMemMapFs()
|
||||
require.NoError(t, afero.WriteFile(fs, "index.html", []byte("index page"), 0644), "WriteFile should have no error.")
|
||||
|
||||
handler := createRouter(&handler{
|
||||
client: mockedClient,
|
||||
content: afero.NewIOFS(fs),
|
||||
config: &Config{Base: "/", Version: "dev"},
|
||||
})
|
||||
handler := createHandler(nil, afero.NewIOFS(fs), Config{Base: "/", Version: "dev"})
|
||||
req, err := http.NewRequest("GET", "/version", nil)
|
||||
require.NoError(t, err, "NewRequest should not return an error.")
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -328,6 +318,137 @@ func Test_createRoutes_version(t *testing.T) {
|
||||
abide.AssertHTTPResponse(t, t.Name(), rr.Result())
|
||||
}
|
||||
|
||||
func Test_createRoutes_username_password(t *testing.T) {
|
||||
|
||||
handler := createHandler(nil, nil, Config{Base: "/", Username: "amir", Password: "password", Key: "key"})
|
||||
req, err := http.NewRequest("GET", "/", nil)
|
||||
require.NoError(t, err, "NewRequest should not return an error.")
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
abide.AssertHTTPResponse(t, t.Name(), rr.Result())
|
||||
}
|
||||
|
||||
func Test_createRoutes_username_password_invalid(t *testing.T) {
|
||||
handler := createHandler(nil, nil, Config{Base: "/", Username: "amir", Password: "password", Key: "key"})
|
||||
req, err := http.NewRequest("GET", "/api/logs/stream?id=123", nil)
|
||||
require.NoError(t, err, "NewRequest should not return an error.")
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
abide.AssertHTTPResponse(t, t.Name(), rr.Result())
|
||||
}
|
||||
|
||||
func Test_createRoutes_username_password_login_happy(t *testing.T) {
|
||||
handler := createHandler(nil, nil, Config{Base: "/", Username: "amir", Password: "password", Key: "key"})
|
||||
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
|
||||
fw, err := writer.CreateFormField("username")
|
||||
require.NoError(t, err, "Creating field should not be error.")
|
||||
_, err = io.Copy(fw, strings.NewReader("amir"))
|
||||
require.NoError(t, err, "Copying field should not result in error.")
|
||||
|
||||
fw, err = writer.CreateFormField("password")
|
||||
require.NoError(t, err, "Creating field should not be error.")
|
||||
_, err = io.Copy(fw, strings.NewReader("password"))
|
||||
require.NoError(t, err, "Copying field should not result in error.")
|
||||
|
||||
writer.Close()
|
||||
|
||||
req, err := http.NewRequest("POST", "/api/validateCredentials", bytes.NewReader(body.Bytes()))
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
|
||||
require.NoError(t, err, "NewRequest should not return an error.")
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, rr.Code, 200)
|
||||
cookie := rr.Header().Get("Set-Cookie")
|
||||
assert.Matches(t, cookie, "session=.+")
|
||||
}
|
||||
|
||||
func Test_createRoutes_username_password_login_failed(t *testing.T) {
|
||||
handler := createHandler(nil, nil, Config{Base: "/", Username: "amir", Password: "password", Key: "key"})
|
||||
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
|
||||
fw, err := writer.CreateFormField("username")
|
||||
require.NoError(t, err, "Creating field should not be error.")
|
||||
_, err = io.Copy(fw, strings.NewReader("amir"))
|
||||
require.NoError(t, err, "Copying field should not result in error.")
|
||||
|
||||
fw, err = writer.CreateFormField("password")
|
||||
require.NoError(t, err, "Creating field should not be error.")
|
||||
_, err = io.Copy(fw, strings.NewReader("bad"))
|
||||
require.NoError(t, err, "Copying field should not result in error.")
|
||||
|
||||
writer.Close()
|
||||
|
||||
req, err := http.NewRequest("POST", "/api/validateCredentials", bytes.NewReader(body.Bytes()))
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
|
||||
require.NoError(t, err, "NewRequest should not return an error.")
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, rr.Code, 401)
|
||||
}
|
||||
|
||||
func Test_createRoutes_username_password_valid_session(t *testing.T) {
|
||||
mockedClient := new(MockedClient)
|
||||
mockedClient.On("FindContainer", "123").Return(docker.Container{ID: "123"}, nil)
|
||||
mockedClient.On("ContainerLogs", mock.Anything, "123", 0).Return(ioutil.NopCloser(strings.NewReader("test data")), io.EOF)
|
||||
handler := createHandler(mockedClient, nil, Config{Base: "/", Username: "amir", Password: "password", Key: "key"})
|
||||
|
||||
// Get cookie first
|
||||
req, err := http.NewRequest("GET", "/api/logs/stream?id=123", nil)
|
||||
require.NoError(t, err, "NewRequest should not return an error.")
|
||||
session, _ := store.Get(req, sessionName)
|
||||
session.Values[authorityKey] = time.Now().Unix()
|
||||
recorder := httptest.NewRecorder()
|
||||
session.Save(req, recorder)
|
||||
cookies := recorder.Result().Cookies()
|
||||
|
||||
// Test with cookie
|
||||
req, err = http.NewRequest("GET", "/api/logs/stream?id=123", nil)
|
||||
require.NoError(t, err, "NewRequest should not return an error.")
|
||||
req.AddCookie(cookies[0])
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
abide.AssertHTTPResponse(t, t.Name(), rr.Result())
|
||||
}
|
||||
|
||||
func Test_createRoutes_username_password_invalid_session(t *testing.T) {
|
||||
mockedClient := new(MockedClient)
|
||||
mockedClient.On("FindContainer", "123").Return(docker.Container{ID: "123"}, nil)
|
||||
mockedClient.On("ContainerLogs", mock.Anything, "123", 0).Return(ioutil.NopCloser(strings.NewReader("test data")), io.EOF)
|
||||
handler := createHandler(mockedClient, nil, Config{Base: "/", Username: "amir", Password: "password", Key: "key"})
|
||||
req, err := http.NewRequest("GET", "/api/logs/stream?id=123", nil)
|
||||
require.NoError(t, err, "NewRequest should not return an error.")
|
||||
req.AddCookie(&http.Cookie{Name: "session", Value: "baddata"})
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, rr.Code, 401)
|
||||
}
|
||||
|
||||
func createHandler(client docker.Client, content fs.FS, config Config) *mux.Router {
|
||||
if client == nil {
|
||||
client = new(MockedClient)
|
||||
}
|
||||
|
||||
if content == nil {
|
||||
fs := afero.NewMemMapFs()
|
||||
afero.WriteFile(fs, "index.html", []byte("index page"), 0644)
|
||||
content = afero.NewIOFS(fs)
|
||||
}
|
||||
|
||||
return createRouter(&handler{
|
||||
client: client,
|
||||
content: content,
|
||||
config: &config,
|
||||
})
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
exit := m.Run()
|
||||
abide.Cleanup()
|
||||
|
||||