Merge branch 'main' of github.com:OliveTin/OliveTin
This commit is contained in:
commit
3ea14f1353
|
|
@ -0,0 +1,44 @@
|
||||||
|
root = "."
|
||||||
|
testdata_dir = "testdata"
|
||||||
|
tmp_dir = "tmp"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
args_bin = []
|
||||||
|
bin = "./OliveTin"
|
||||||
|
cmd = "go build -o OliveTin github.com/OliveTin/OliveTin/cmd/OliveTin"
|
||||||
|
delay = 1
|
||||||
|
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
|
||||||
|
exclude_file = []
|
||||||
|
exclude_regex = ["_test.go"]
|
||||||
|
exclude_unchanged = false
|
||||||
|
follow_symlink = false
|
||||||
|
full_bin = ""
|
||||||
|
include_dir = []
|
||||||
|
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||||
|
include_file = []
|
||||||
|
kill_delay = "0s"
|
||||||
|
log = "build-errors.log"
|
||||||
|
poll = false
|
||||||
|
poll_interval = 0
|
||||||
|
rerun = false
|
||||||
|
rerun_delay = 500
|
||||||
|
send_interrupt = false
|
||||||
|
stop_on_error = true
|
||||||
|
|
||||||
|
[color]
|
||||||
|
app = ""
|
||||||
|
build = "yellow"
|
||||||
|
main = "magenta"
|
||||||
|
runner = "green"
|
||||||
|
watcher = "cyan"
|
||||||
|
|
||||||
|
[log]
|
||||||
|
main_only = false
|
||||||
|
time = false
|
||||||
|
|
||||||
|
[misc]
|
||||||
|
clean_on_exit = false
|
||||||
|
|
||||||
|
[screen]
|
||||||
|
clear_on_rebuild = false
|
||||||
|
keep_scroll = true
|
||||||
|
|
@ -39,7 +39,7 @@ If possible, please copy and paste your OliveTin logs from when the error happen
|
||||||
**Screenshot of WebDeveloper console logs**
|
**Screenshot of WebDeveloper console logs**
|
||||||
|
|
||||||
If you know how, and if you think it's relevant, a screenshot of the
|
If you know how, and if you think it's relevant, a screenshot of the
|
||||||
WebDeveloper console from when you clicked a button is often really helpful.
|
WebDeveloper console from when you clicked a button is often really helpful.
|
||||||
|
|
||||||
**Anything else?**
|
**Anything else?**
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ First of all, thank you for considering to raise a pull request!
|
||||||
|
|
||||||
Don’t be afraid to ask for advice before working on a contribution. If you’re thinking about a bigger change, especially that might affect the core working or architecture, it’s almost essential to talk and ask about what you’re planning might affect things. Some of the larger future plans may not be documented well so it’s difficult to understand how your change might affect the general direction and roadmap of this project without asking.
|
Don’t be afraid to ask for advice before working on a contribution. If you’re thinking about a bigger change, especially that might affect the core working or architecture, it’s almost essential to talk and ask about what you’re planning might affect things. Some of the larger future plans may not be documented well so it’s difficult to understand how your change might affect the general direction and roadmap of this project without asking.
|
||||||
|
|
||||||
The preferred way to communicate is probably via Discord or GitHub issues.
|
The preferred way to communicate is probably via Discord or GitHub issues.
|
||||||
|
|
||||||
Helpful information to understand the project can be found here: [CONTRIBUTING](https://github.com/OliveTin/OliveTin/blob/main/CONTRIBUTING.adoc)
|
Helpful information to understand the project can be found here: [CONTRIBUTING](https://github.com/OliveTin/OliveTin/blob/main/CONTRIBUTING.adoc)
|
||||||
|
|
||||||
|
|
@ -13,10 +13,10 @@ Helpful information to understand the project can be found here: [CONTRIBUTING](
|
||||||
# Checklist
|
# Checklist
|
||||||
Please put a X in the boxes as evidence of reading through the checklist.
|
Please put a X in the boxes as evidence of reading through the checklist.
|
||||||
|
|
||||||
- [ ] I have forked the project, and raised this PR on a feature branch.
|
- [ ] I have forked the project, and raised this PR on a feature branch.
|
||||||
- [ ] `make githooks` has been run, and my git commit message was accepted by the git hook.
|
- [ ] `make githooks` has been run, and my git commit message was accepted by the git hook.
|
||||||
- [ ] `make daemon-compile` runs without any issues.
|
- [ ] `make daemon-compile` runs without any issues.
|
||||||
- [ ] `make daemon-codestyle` runs without any issues.
|
- [ ] `make daemon-codestyle` runs without any issues.
|
||||||
- [ ] `make daemon-unittests` runs without any issues.
|
- [ ] `make daemon-unittests` runs without any issues.
|
||||||
- [ ] `make webui-codestyle` runs without any issues.
|
- [ ] `make webui-codestyle` runs without any issues.
|
||||||
- [ ] I understand and accept the [AGPL-3.0 license](LICENSE) and [code of conduct](CODE_OF_CONDUCT.md), and my contributions fall under these.
|
- [ ] I understand and accept the [AGPL-3.0 license](LICENSE) and [code of conduct](CODE_OF_CONDUCT.md), and my contributions fall under these.
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ jobs:
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '>=1.18.0'
|
go-version: '^1.18.0'
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
- name: grpc
|
- name: grpc
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,10 @@ jobs:
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '^1.16.0'
|
go-version: '^1.18.0'
|
||||||
|
cache: true
|
||||||
|
|
||||||
- name: deps
|
- name: deps
|
||||||
run: make grpc
|
run: make grpc
|
||||||
|
|
|
||||||
|
|
@ -9,3 +9,4 @@ reports
|
||||||
releases/
|
releases/
|
||||||
dist/
|
dist/
|
||||||
installation-id.txt
|
installation-id.txt
|
||||||
|
tmp/
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ builds:
|
||||||
|
|
||||||
goarm:
|
goarm:
|
||||||
- 5 # For old RPIs
|
- 5 # For old RPIs
|
||||||
- 6
|
- 6
|
||||||
- 7
|
- 7
|
||||||
|
|
||||||
main: cmd/OliveTin/main.go
|
main: cmd/OliveTin/main.go
|
||||||
|
|
@ -62,10 +62,10 @@ changelog:
|
||||||
- '^refactor:'
|
- '^refactor:'
|
||||||
|
|
||||||
archives:
|
archives:
|
||||||
-
|
-
|
||||||
format: tar.gz
|
format: tar.gz
|
||||||
|
|
||||||
files:
|
files:
|
||||||
- config.yaml
|
- config.yaml
|
||||||
- LICENSE
|
- LICENSE
|
||||||
- README.md
|
- README.md
|
||||||
|
|
@ -225,15 +225,14 @@ release:
|
||||||
## Container images ([on Docker Hub](https://hub.docker.com/r/jamesread/olivetin/tags?page=1&ordering=last_updated))
|
## Container images ([on Docker Hub](https://hub.docker.com/r/jamesread/olivetin/tags?page=1&ordering=last_updated))
|
||||||
|
|
||||||
- `docker pull docker.io/jamesread/olivetin:{{ .Version }}`
|
- `docker pull docker.io/jamesread/olivetin:{{ .Version }}`
|
||||||
|
|
||||||
## Upgrade warnings, or breaking changes
|
## Upgrade warnings, or breaking changes
|
||||||
|
|
||||||
- No such issues between the last release and this version.
|
- No such issues between the last release and this version.
|
||||||
|
|
||||||
## Useful links
|
## Useful links
|
||||||
|
|
||||||
- [Which download do I need?](https://docs.olivetin.app/choose-package.html)
|
- [Which download do I need?](https://docs.olivetin.app/choose-package.html)
|
||||||
- [Ask for help and chat with others users in the Discord community](https://discord.gg/jhYWWpNJ3v)
|
- [Ask for help and chat with others users in the Discord community](https://discord.gg/jhYWWpNJ3v)
|
||||||
|
|
||||||
Thanks for your interest in OliveTin!
|
Thanks for your interest in OliveTin!
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
# See https://pre-commit.com for more information
|
||||||
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v3.2.0
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: check-yaml
|
||||||
|
- id: check-added-large-files
|
||||||
|
|
@ -7,9 +7,9 @@ of multiple projects, your time and interest in contributing is most welcome.
|
||||||
If you're not sure where to get started, raise an issue in the project.
|
If you're not sure where to get started, raise an issue in the project.
|
||||||
|
|
||||||
Ideas may be discussed, purely on their merits and issues. Our Code of Conduct
|
Ideas may be discussed, purely on their merits and issues. Our Code of Conduct
|
||||||
(CoC) is straightforward - it's important that contributors feel comfortable in
|
(CoC) is straightforward - it's important that contributors feel comfortable in
|
||||||
discussion throughout the whole process. This project respects the
|
discussion throughout the whole process. This project respects the
|
||||||
link:https://www.kernel.org/doc/html/latest/process/code-of-conduct.html[Linux Kernel code of conduct].
|
link:https://www.kernel.org/doc/html/latest/process/code-of-conduct.html[Linux Kernel code of conduct].
|
||||||
|
|
||||||
== If you're not sure, ask!
|
== If you're not sure, ask!
|
||||||
|
|
||||||
|
|
@ -18,19 +18,19 @@ contribution. If you're thinking about a bigger change, especially that might
|
||||||
affect the core working or architecture, it's almost essential to talk and ask
|
affect the core working or architecture, it's almost essential to talk and ask
|
||||||
about what you're planning might affect things. Some of the larger future plans may not be
|
about what you're planning might affect things. Some of the larger future plans may not be
|
||||||
documented well so it's difficult to understand how your change might affect
|
documented well so it's difficult to understand how your change might affect
|
||||||
the general direction and roadmap of this project without asking.
|
the general direction and roadmap of this project without asking.
|
||||||
|
|
||||||
The preferred way to communicate is probably via Discord or GitHub issues.
|
The preferred way to communicate is probably via Discord or GitHub issues.
|
||||||
|
|
||||||
=== Dev environment setup and clean build - Fedora
|
=== Dev environment setup and clean build - Fedora
|
||||||
|
|
||||||
```
|
```
|
||||||
dnf install git go protobuf-compiler make -y
|
dnf install git go protobuf-compiler make -y
|
||||||
git clone https://github.com/OliveTin/OliveTin.git
|
git clone https://github.com/OliveTin/OliveTin.git
|
||||||
cd OliveTin
|
cd OliveTin
|
||||||
|
|
||||||
# `make grpc` will also run `make go-tools`, which installs "buf". This binary
|
# `make grpc` will also run `make go-tools`, which installs "buf". This binary
|
||||||
# will be put in your GOPATH/bin/, which should be on your path. buf is used to
|
# will be put in your GOPATH/bin/, which should be on your path. buf is used to
|
||||||
# generate the protobuf / grpc stubs.
|
# generate the protobuf / grpc stubs.
|
||||||
make grpc
|
make grpc
|
||||||
make
|
make
|
||||||
|
|
@ -39,10 +39,10 @@ make
|
||||||
|
|
||||||
=== Getting started to contribute;
|
=== Getting started to contribute;
|
||||||
|
|
||||||
The project layout is reasonably straightforward;
|
The project layout is reasonably straightforward;
|
||||||
|
|
||||||
* See the `Makefile` for common targets. This project was originally created on top of Fedora, but it should be usable on Debian/your faveourite distro with minor changes (if any).
|
* See the `Makefile` for common targets. This project was originally created on top of Fedora, but it should be usable on Debian/your faveourite distro with minor changes (if any).
|
||||||
* The API is defined in protobuf+grpc - you will need to `make grpc`.
|
* The API is defined in protobuf+grpc - you will need to `make grpc`.
|
||||||
* The Go daemon is built from the `cmd` and `internal` directories mostly.
|
* The Go daemon is built from the `cmd` and `internal` directories mostly.
|
||||||
* The webui is just a single page application with a bit of Javascript in the `webui` directory. This can happily be hosted on another webserver.
|
* The webui is just a single page application with a bit of Javascript in the `webui` directory. This can happily be hosted on another webserver.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,16 @@ LABEL org.opencontainers.image.source https://github.com/OliveTin/OliveTin
|
||||||
LABEL org.opencontainers.image.title=OliveTin
|
LABEL org.opencontainers.image.title=OliveTin
|
||||||
|
|
||||||
RUN mkdir -p /config /var/www/olivetin \
|
RUN mkdir -p /config /var/www/olivetin \
|
||||||
&& microdnf install -y --nodocs --noplugins --setopt=keepcache=0 --setopt=install_weak_deps=0 \
|
&& microdnf install -y --nodocs --noplugins --setopt=keepcache=0 --setopt=install_weak_deps=0 \
|
||||||
iputils \
|
iputils \
|
||||||
openssh-clients \
|
openssh-clients \
|
||||||
shadow-utils \
|
shadow-utils \
|
||||||
docker \
|
docker \
|
||||||
&& microdnf clean all
|
&& microdnf clean all
|
||||||
|
|
||||||
RUN useradd --system --create-home olivetin -u 1000
|
RUN useradd --system --create-home olivetin -u 1000
|
||||||
|
|
||||||
EXPOSE 1337/tcp
|
EXPOSE 1337/tcp
|
||||||
|
|
||||||
VOLUME /config
|
VOLUME /config
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,14 @@ LABEL org.opencontainers.image.title=OliveTin
|
||||||
|
|
||||||
RUN mkdir -p /config /var/www/olivetin \
|
RUN mkdir -p /config /var/www/olivetin \
|
||||||
&& \
|
&& \
|
||||||
microdnf install -y --nodocs --noplugins --setopt=keepcache=0 --setopt=install_weak_deps=0 \
|
microdnf install -y --nodocs --noplugins --setopt=keepcache=0 --setopt=install_weak_deps=0 \
|
||||||
iputils \
|
iputils \
|
||||||
shadow-utils \
|
shadow-utils \
|
||||||
openssh-clients
|
openssh-clients
|
||||||
|
|
||||||
RUN useradd --system --create-home olivetin -u 1000
|
RUN useradd --system --create-home olivetin -u 1000
|
||||||
|
|
||||||
EXPOSE 1337/tcp
|
EXPOSE 1337/tcp
|
||||||
|
|
||||||
VOLUME /config
|
VOLUME /config
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,14 @@ LABEL org.opencontainers.image.title=OliveTin
|
||||||
|
|
||||||
RUN mkdir -p /config /var/www/olivetin \
|
RUN mkdir -p /config /var/www/olivetin \
|
||||||
&& \
|
&& \
|
||||||
microdnf install -y --nodocs --noplugins --setopt=keepcache=0 --setopt=install_weak_deps=0 \
|
microdnf install -y --nodocs --noplugins --setopt=keepcache=0 --setopt=install_weak_deps=0 \
|
||||||
iputils \
|
iputils \
|
||||||
shadow-utils \
|
shadow-utils \
|
||||||
openssh-clients
|
openssh-clients
|
||||||
|
|
||||||
RUN useradd --system --create-home olivetin -u 1000
|
RUN useradd --system --create-home olivetin -u 1000
|
||||||
|
|
||||||
EXPOSE 1337/tcp
|
EXPOSE 1337/tcp
|
||||||
|
|
||||||
VOLUME /config
|
VOLUME /config
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ pipeline {
|
||||||
sh 'make go-tools'
|
sh 'make go-tools'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stage('Compile') {
|
stage('Compile') {
|
||||||
steps {
|
steps {
|
||||||
withEnv(["PATH+GO=/root/go/bin/"]) {
|
withEnv(["PATH+GO=/root/go/bin/"]) {
|
||||||
|
|
@ -25,9 +25,9 @@ pipeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stage ('Post-Compile') {
|
stage ('Post-Compile') {
|
||||||
parallel {
|
parallel {
|
||||||
stage('Codestyle') {
|
stage('Codestyle') {
|
||||||
steps {
|
steps {
|
||||||
withEnv(["PATH+GO=/root/go/bin/"]) {
|
withEnv(["PATH+GO=/root/go/bin/"]) {
|
||||||
|
|
@ -45,6 +45,6 @@ pipeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
14
Makefile
14
Makefile
|
|
@ -1,9 +1,9 @@
|
||||||
compile: daemon-compile-x64-lin
|
compile: daemon-compile-x64-lin
|
||||||
|
|
||||||
daemon-compile-armhf:
|
daemon-compile-armhf:
|
||||||
GOARCH=arm GOARM=6 go build -o OliveTin.armhf github.com/OliveTin/OliveTin/cmd/OliveTin
|
GOARCH=arm GOARM=6 go build -o OliveTin.armhf github.com/OliveTin/OliveTin/cmd/OliveTin
|
||||||
|
|
||||||
daemon-compile-x64-lin:
|
daemon-compile-x64-lin:
|
||||||
GOOS=linux go build -o OliveTin github.com/OliveTin/OliveTin/cmd/OliveTin
|
GOOS=linux go build -o OliveTin github.com/OliveTin/OliveTin/cmd/OliveTin
|
||||||
|
|
||||||
daemon-compile-x64-win:
|
daemon-compile-x64-win:
|
||||||
|
|
@ -14,7 +14,7 @@ daemon-compile: daemon-compile-armhf daemon-compile-x64-lin daemon-compile-x64-w
|
||||||
daemon-codestyle:
|
daemon-codestyle:
|
||||||
go fmt ./...
|
go fmt ./...
|
||||||
go vet ./...
|
go vet ./...
|
||||||
gocyclo -over 4 cmd internal
|
gocyclo -over 4 cmd internal
|
||||||
gocritic check ./...
|
gocritic check ./...
|
||||||
|
|
||||||
daemon-unittests:
|
daemon-unittests:
|
||||||
|
|
@ -24,7 +24,7 @@ daemon-unittests:
|
||||||
|
|
||||||
githooks:
|
githooks:
|
||||||
cp -v .githooks/* .git/hooks/
|
cp -v .githooks/* .git/hooks/
|
||||||
|
|
||||||
go-tools:
|
go-tools:
|
||||||
go install "github.com/bufbuild/buf/cmd/buf"
|
go install "github.com/bufbuild/buf/cmd/buf"
|
||||||
go install "github.com/fzipp/gocyclo/cmd/gocyclo"
|
go install "github.com/fzipp/gocyclo/cmd/gocyclo"
|
||||||
|
|
@ -37,7 +37,7 @@ go-tools:
|
||||||
grpc: go-tools
|
grpc: go-tools
|
||||||
buf generate
|
buf generate
|
||||||
|
|
||||||
dist: protoc
|
dist: protoc
|
||||||
|
|
||||||
protoc:
|
protoc:
|
||||||
protoc --go_out=. --go-grpc_out=. --grpc-gateway_out=. -I .:/usr/include/ OliveTin.proto
|
protoc --go_out=. --go-grpc_out=. --grpc-gateway_out=. -I .:/usr/include/ OliveTin.proto
|
||||||
|
|
@ -54,7 +54,7 @@ podman-container:
|
||||||
integration-tests-docker-image:
|
integration-tests-docker-image:
|
||||||
docker rm -f olivetin && docker rmi -f olivetin
|
docker rm -f olivetin && docker rmi -f olivetin
|
||||||
docker build -t olivetin:latest .
|
docker build -t olivetin:latest .
|
||||||
docker create --name olivetin -p 1337:1337 -v `pwd`/integration-tests/configs/:/config/ olivetin
|
docker create --name olivetin -p 1337:1337 -v `pwd`/integration-tests/configs/:/config/ olivetin
|
||||||
|
|
||||||
devrun: compile
|
devrun: compile
|
||||||
killall OliveTin || true
|
killall OliveTin || true
|
||||||
|
|
@ -70,4 +70,4 @@ webui-codestyle:
|
||||||
clean:
|
clean:
|
||||||
rm -rf dist OliveTin OliveTin.armhf OliveTin.exe reports gen
|
rm -rf dist OliveTin OliveTin.armhf OliveTin.exe reports gen
|
||||||
|
|
||||||
.PHONY: grpc
|
.PHONY: grpc
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,16 @@ message Action {
|
||||||
string id = 1;
|
string id = 1;
|
||||||
string title = 2;
|
string title = 2;
|
||||||
string icon = 3;
|
string icon = 3;
|
||||||
bool canExec = 4;
|
bool can_exec = 4;
|
||||||
|
|
||||||
repeated ActionArgument arguments = 5;
|
repeated ActionArgument arguments = 5;
|
||||||
|
bool popup_on_start = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ActionArgument {
|
message ActionArgument {
|
||||||
string name = 1;
|
string name = 1;
|
||||||
string title = 2;
|
string title = 2;
|
||||||
string type = 3;
|
string type = 3;
|
||||||
string defaultValue = 4;
|
string default_value = 4;
|
||||||
|
|
||||||
repeated ActionArgumentChoice choices = 5;
|
repeated ActionArgumentChoice choices = 5;
|
||||||
|
|
||||||
|
|
@ -44,9 +44,11 @@ message GetDashboardComponentsResponse {
|
||||||
message GetDashboardComponentsRequest {}
|
message GetDashboardComponentsRequest {}
|
||||||
|
|
||||||
message StartActionRequest {
|
message StartActionRequest {
|
||||||
string actionName = 1;
|
string action_name = 1;
|
||||||
|
|
||||||
repeated StartActionArgument arguments = 2;
|
repeated StartActionArgument arguments = 2;
|
||||||
|
|
||||||
|
string uuid = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message StartActionArgument {
|
message StartActionArgument {
|
||||||
|
|
@ -55,22 +57,28 @@ message StartActionArgument {
|
||||||
}
|
}
|
||||||
|
|
||||||
message StartActionResponse {
|
message StartActionResponse {
|
||||||
LogEntry logEntry = 1;
|
string execution_uuid = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetLogsRequest{};
|
message GetLogsRequest{};
|
||||||
|
|
||||||
message LogEntry {
|
message LogEntry {
|
||||||
string datetime = 1;
|
string datetime_started = 1;
|
||||||
string actionTitle = 2;
|
string action_title = 2;
|
||||||
string stdout = 3;
|
string stdout = 3;
|
||||||
string stderr = 4;
|
string stderr = 4;
|
||||||
bool timedOut = 5;
|
bool timed_out = 5;
|
||||||
int32 exitCode = 6;
|
int32 exit_code = 6;
|
||||||
string user = 7;
|
string user = 7;
|
||||||
string userClass = 8;
|
string user_class = 8;
|
||||||
string actionIcon = 9;
|
string action_icon = 9;
|
||||||
repeated string tags = 10;
|
repeated string tags = 10;
|
||||||
|
string execution_uuid = 11;
|
||||||
|
string datetime_finished = 12;
|
||||||
|
string uuid = 13;
|
||||||
|
bool execution_started = 14;
|
||||||
|
bool execution_finished = 15;
|
||||||
|
bool blocked = 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetLogsResponse {
|
message GetLogsResponse {
|
||||||
|
|
@ -87,10 +95,26 @@ message ValidateArgumentTypeResponse {
|
||||||
string description = 2;
|
string description = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message WatchExecutionRequest {
|
||||||
|
string execution_uuid = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WatchExecutionUpdate {
|
||||||
|
string update = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExecutionStatusRequest {
|
||||||
|
string execution_uuid = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExecutionStatusResponse {
|
||||||
|
LogEntry log_entry = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message WhoAmIRequest {}
|
message WhoAmIRequest {}
|
||||||
|
|
||||||
message WhoAmIResponse {
|
message WhoAmIResponse {
|
||||||
string authenticatedUser = 1;
|
string authenticated_user = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SosReportRequest {}
|
message SosReportRequest {}
|
||||||
|
|
@ -99,7 +123,7 @@ message SosReportResponse {
|
||||||
string alert = 1;
|
string alert = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
service OliveTinApi {
|
service OliveTinApiService {
|
||||||
rpc GetDashboardComponents(GetDashboardComponentsRequest) returns (GetDashboardComponentsResponse) {
|
rpc GetDashboardComponents(GetDashboardComponentsRequest) returns (GetDashboardComponentsResponse) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
get: "/api/GetDashboardComponents"
|
get: "/api/GetDashboardComponents"
|
||||||
|
|
@ -113,6 +137,13 @@ service OliveTinApi {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpc ExecutionStatus(ExecutionStatusRequest) returns (ExecutionStatusResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/api/ExecutionStatus"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
rpc GetLogs(GetLogsRequest) returns (GetLogsResponse) {
|
rpc GetLogs(GetLogsRequest) returns (GetLogsResponse) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
get: "/api/GetLogs"
|
get: "/api/GetLogs"
|
||||||
|
|
|
||||||
29
README.md
29
README.md
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<img alt = "project logo" src = "https://github.com/OliveTin/OliveTin/blob/main/webui/OliveTinLogo.png" align = "right" width = "160px" />
|
<img alt = "project logo" src = "https://github.com/OliveTin/OliveTin/blob/main/webui/OliveTinLogo.png" align = "right" width = "160px" />
|
||||||
|
|
||||||
OliveTin gives **safe** and **simple** access to predefined shell commands from a web interface.
|
OliveTin gives **safe** and **simple** access to predefined shell commands from a web interface.
|
||||||
|
|
||||||
[](https://discord.gg/jhYWWpNJ3v)
|
[](https://discord.gg/jhYWWpNJ3v)
|
||||||
[](https://github.com/awesome-selfhosted/awesome-selfhosted#automation)
|
[](https://github.com/awesome-selfhosted/awesome-selfhosted#automation)
|
||||||
|
|
@ -11,7 +11,7 @@ OliveTin gives **safe** and **simple** access to predefined shell commands from
|
||||||
[](https://goreportcard.com/report/github.com/OliveTin/OliveTin)
|
[](https://goreportcard.com/report/github.com/OliveTin/OliveTin)
|
||||||
[](https://github.com/OliveTin/OliveTin/actions/workflows/build-snapshot.yml)
|
[](https://github.com/OliveTin/OliveTin/actions/workflows/build-snapshot.yml)
|
||||||
|
|
||||||
## Use cases
|
## Use cases
|
||||||
|
|
||||||
**Safely** give access to commands, for less technical people;
|
**Safely** give access to commands, for less technical people;
|
||||||
|
|
||||||
|
|
@ -34,10 +34,10 @@ OliveTin gives **safe** and **simple** access to predefined shell commands from
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* **Responsive, touch-friendly UI** - great for tablets and mobile
|
* **Responsive, touch-friendly UI** - great for tablets and mobile
|
||||||
* **Super simple config in YAML** - because if it's not YAML now-a-days, it's not "cloud native" :-)
|
* **Super simple config in YAML** - because if it's not YAML now-a-days, it's not "cloud native" :-)
|
||||||
* **Dark mode** - for those of you that roll that way.
|
* **Dark mode** - for those of you that roll that way.
|
||||||
* **Accessible** - passes all the accessibility checks in Firefox, and issues with accessibility are taken seriously.
|
* **Accessible** - passes all the accessibility checks in Firefox, and issues with accessibility are taken seriously.
|
||||||
* **Container** - available for quickly testing and getting it up and running, great for the selfhosted community.
|
* **Container** - available for quickly testing and getting it up and running, great for the selfhosted community.
|
||||||
* **Integrate with anything** - OliveTin just runs Linux shell commands, so theoretially you could integrate with a bunch of stuff just by using curl, ping, etc. However, writing your own shell scripts is a great way to extend OliveTin.
|
* **Integrate with anything** - OliveTin just runs Linux shell commands, so theoretially you could integrate with a bunch of stuff just by using curl, ping, etc. However, writing your own shell scripts is a great way to extend OliveTin.
|
||||||
* **Lightweight on resources** - uses only a few MB of RAM and barely any CPU. Written in Go, with a web interface written as a modern, responsive, Single Page App that uses the REST/gRPC API.
|
* **Lightweight on resources** - uses only a few MB of RAM and barely any CPU. Written in Go, with a web interface written as a modern, responsive, Single Page App that uses the REST/gRPC API.
|
||||||
* **Good amount of unit tests and style checks** - helps potential contributors be consistent, and helps with maintainability.
|
* **Good amount of unit tests and style checks** - helps potential contributors be consistent, and helps with maintainability.
|
||||||
|
|
@ -48,17 +48,17 @@ Desktop web browser;
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Desktop web browser (dark mode);
|
Desktop web browser (dark mode);
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Mobile screen size (responsive layout);
|
Mobile screen size (responsive layout);
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
All documentation can be found at http://docs.olivetin.app . This includes installation and usage guide, etc.
|
All documentation can be found at http://docs.olivetin.app . This includes installation and usage guide, etc.
|
||||||
|
|
||||||
### Quickstart reference for `config.yaml`
|
### Quickstart reference for `config.yaml`
|
||||||
|
|
||||||
|
|
@ -75,19 +75,19 @@ Put this `config.yaml` in `/etc/OliveTin/` if you're running a standard service,
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# Listen on all addresses available, port 1337
|
# Listen on all addresses available, port 1337
|
||||||
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
|
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
|
||||||
|
|
||||||
# Choose from INFO (default), WARN and DEBUG
|
# Choose from INFO (default), WARN and DEBUG
|
||||||
logLevel: "INFO"
|
logLevel: "INFO"
|
||||||
|
|
||||||
# Actions (buttons) to show up on the WebUI:
|
# Actions (buttons) to show up on the WebUI:
|
||||||
actions:
|
actions:
|
||||||
# Docs: https://docs.olivetin.app/action-container-control.html
|
# Docs: https://docs.olivetin.app/action-container-control.html
|
||||||
- title: Restart Plex
|
- title: Restart Plex
|
||||||
icon: restart
|
icon: restart
|
||||||
shell: docker restart plex
|
shell: docker restart plex
|
||||||
|
|
||||||
# This will send 1 ping
|
# This will send 1 ping
|
||||||
# Docs: https://docs.olivetin.app/action-ping.html
|
# Docs: https://docs.olivetin.app/action-ping.html
|
||||||
- title: Ping host
|
- title: Ping host
|
||||||
shell: ping {{ host }} -c {{ count }}
|
shell: ping {{ host }} -c {{ count }}
|
||||||
|
|
@ -102,7 +102,7 @@ actions:
|
||||||
title: Count
|
title: Count
|
||||||
type: int
|
type: int
|
||||||
default: 1
|
default: 1
|
||||||
|
|
||||||
# Restart http on host "webserver1"
|
# Restart http on host "webserver1"
|
||||||
# Docs: https://docs.olivetin.app/action-ssh.html
|
# Docs: https://docs.olivetin.app/action-ssh.html
|
||||||
- title: restart httpd
|
- title: restart httpd
|
||||||
|
|
@ -111,4 +111,3 @@ actions:
|
||||||
```
|
```
|
||||||
|
|
||||||
A full example config can be found at in this repository - [config.yaml](https://github.com/OliveTin/OliveTin/blob/main/config.yaml).
|
A full example config can be found at in this repository - [config.yaml](https://github.com/OliveTin/OliveTin/blob/main/config.yaml).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## Supported Versions
|
## Supported Versions
|
||||||
|
|
||||||
Currently, only the `main` branch is "supported".
|
Currently, only the `main` branch is "supported".
|
||||||
|
|
||||||
| Version | Supported |
|
| Version | Supported |
|
||||||
| ------- | ------------------ |
|
| ------- | ------------------ |
|
||||||
|
|
@ -10,4 +10,4 @@ Currently, only the `main` branch is "supported".
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
Please email `contact@jread.com` for responsible disclosure. Accepted issues will be made public once patched, and you will be given credit.
|
Please email `contact@jread.com` for responsible disclosure. Accepted issues will be made public once patched, and you will be given credit.
|
||||||
|
|
|
||||||
|
|
@ -17,4 +17,3 @@ plugins:
|
||||||
|
|
||||||
# - name: openapiv2
|
# - name: openapiv2
|
||||||
# out: reports/openapiv2
|
# out: reports/openapiv2
|
||||||
|
|
||||||
|
|
|
||||||
2
buf.yaml
2
buf.yaml
|
|
@ -1,5 +1,5 @@
|
||||||
version: v1
|
version: v1
|
||||||
deps:
|
deps:
|
||||||
- buf.build/googleapis/googleapis
|
- buf.build/googleapis/googleapis
|
||||||
lint:
|
lint:
|
||||||
use:
|
use:
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/OliveTin/OliveTin/internal/oncron"
|
"github.com/OliveTin/OliveTin/internal/oncron"
|
||||||
"github.com/OliveTin/OliveTin/internal/onstartup"
|
"github.com/OliveTin/OliveTin/internal/onstartup"
|
||||||
updatecheck "github.com/OliveTin/OliveTin/internal/updatecheck"
|
updatecheck "github.com/OliveTin/OliveTin/internal/updatecheck"
|
||||||
|
"github.com/OliveTin/OliveTin/internal/websocket"
|
||||||
|
|
||||||
"github.com/OliveTin/OliveTin/internal/httpservers"
|
"github.com/OliveTin/OliveTin/internal/httpservers"
|
||||||
|
|
||||||
|
|
@ -110,6 +111,7 @@ func main() {
|
||||||
log.Debugf("Config: %+v", cfg)
|
log.Debugf("Config: %+v", cfg)
|
||||||
|
|
||||||
executor := executor.DefaultExecutor()
|
executor := executor.DefaultExecutor()
|
||||||
|
executor.AddListener(websocket.ExecutionListener)
|
||||||
|
|
||||||
go onstartup.Execute(cfg, executor)
|
go onstartup.Execute(cfg, executor)
|
||||||
go oncron.Schedule(cfg, executor)
|
go oncron.Schedule(cfg, executor)
|
||||||
|
|
|
||||||
24
config.yaml
24
config.yaml
|
|
@ -1,25 +1,30 @@
|
||||||
# There is a built-in micro proxy that will host the webui and REST API all on
|
# There is a built-in micro proxy that will host the webui and REST API all on
|
||||||
# one port (this is called the "Single HTTP Frontend") and means you just need
|
# one port (this is called the "Single HTTP Frontend") and means you just need
|
||||||
# one open port in the container/firewalls/etc.
|
# one open port in the container/firewalls/etc.
|
||||||
#
|
#
|
||||||
# Listen on all addresses available, port 1337
|
# Listen on all addresses available, port 1337
|
||||||
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
|
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
|
||||||
|
|
||||||
# Choose from INFO (default), WARN and DEBUG
|
# Choose from INFO (default), WARN and DEBUG
|
||||||
logLevel: "INFO"
|
logLevel: "INFO"
|
||||||
|
|
||||||
# Actions (buttons) to show up on the WebUI:
|
# Actions (buttons) to show up on the WebUI:
|
||||||
actions:
|
actions:
|
||||||
# This will run a simple script that you create.
|
# This will run a simple script that you create.
|
||||||
- title: Run backup script
|
- title: Run backup script
|
||||||
shell: /opt/backupScript.sh
|
shell: /opt/backupScript.sh
|
||||||
|
maxConcurrent: 1
|
||||||
icon: backup
|
icon: backup
|
||||||
|
|
||||||
|
- title: date
|
||||||
|
shell: date
|
||||||
|
|
||||||
# This will send 1 ping (-c 1)
|
# This will send 1 ping (-c 1)
|
||||||
# Docs: https://docs.olivetin.app/action-ping.html
|
# Docs: https://docs.olivetin.app/action-ping.html
|
||||||
- title: Ping host
|
- title: Ping host
|
||||||
shell: ping {{ host }} -c {{ count }}
|
shell: ping {{ host }} -c {{ count }}
|
||||||
icon: ping
|
icon: ping
|
||||||
|
timeout: 100
|
||||||
arguments:
|
arguments:
|
||||||
- name: host
|
- name: host
|
||||||
title: host
|
title: host
|
||||||
|
|
@ -32,15 +37,15 @@ actions:
|
||||||
type: int
|
type: int
|
||||||
default: 1
|
default: 1
|
||||||
description: How many times to do you want to ping?
|
description: How many times to do you want to ping?
|
||||||
|
|
||||||
# Restart lightdm on host "server1"
|
# Restart lightdm on host "server1"
|
||||||
# Docs: https://docs.olivetin.app/action-ping.html
|
# Docs: https://docs.olivetin.app/action-ping.html
|
||||||
- title: restart httpd
|
- title: restart httpd
|
||||||
icon: restart
|
icon: restart
|
||||||
shell: ssh root@server1 'service httpd restart'
|
shell: ssh root@server1 'service httpd restart'
|
||||||
|
|
||||||
# OliveTin can run long-running jobs like Ansible playbooks.
|
# OliveTin can run long-running jobs like Ansible playbooks.
|
||||||
#
|
#
|
||||||
# For such jobs, you will need to install ansible-playbook on the host where
|
# For such jobs, you will need to install ansible-playbook on the host where
|
||||||
# you are running OliveTin, or in the container.
|
# you are running OliveTin, or in the container.
|
||||||
#
|
#
|
||||||
|
|
@ -51,7 +56,7 @@ actions:
|
||||||
timeout: 120
|
timeout: 120
|
||||||
|
|
||||||
# OliveTin can control containers - docker is just a command line app.
|
# OliveTin can control containers - docker is just a command line app.
|
||||||
#
|
#
|
||||||
# However, if you are running in a container you will need to do some setup,
|
# However, if you are running in a container you will need to do some setup,
|
||||||
# see the docs below.
|
# see the docs below.
|
||||||
#
|
#
|
||||||
|
|
@ -76,4 +81,3 @@ actions:
|
||||||
shell: sleep 5
|
shell: sleep 5
|
||||||
timeout: 5
|
timeout: 5
|
||||||
icon: "😪"
|
icon: "😪"
|
||||||
|
|
||||||
|
|
|
||||||
93
go.mod
93
go.mod
|
|
@ -3,41 +3,45 @@ module github.com/OliveTin/OliveTin
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bufbuild/buf v1.15.1
|
github.com/bufbuild/buf v1.26.0
|
||||||
github.com/fsnotify/fsnotify v1.6.0
|
github.com/fsnotify/fsnotify v1.6.0
|
||||||
github.com/fzipp/gocyclo v0.6.0
|
github.com/fzipp/gocyclo v0.6.0
|
||||||
github.com/go-critic/go-critic v0.7.0
|
github.com/go-critic/go-critic v0.8.2
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2
|
github.com/gorilla/websocket v1.4.1
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.2
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/viper v1.15.0
|
github.com/spf13/viper v1.15.0
|
||||||
github.com/stretchr/testify v1.8.2
|
github.com/stretchr/testify v1.8.4
|
||||||
golang.org/x/exp v0.0.0-20230307190834-24139beb5833
|
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1
|
||||||
google.golang.org/grpc v1.53.0
|
google.golang.org/genproto/googleapis/api v0.0.0-20230807174057-1744710a1577
|
||||||
|
google.golang.org/grpc v1.57.0
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0
|
||||||
google.golang.org/protobuf v1.30.0
|
google.golang.org/protobuf v1.31.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||||
github.com/bufbuild/connect-go v1.5.2 // indirect
|
github.com/bufbuild/connect-go v1.10.0 // indirect
|
||||||
github.com/bufbuild/protocompile v0.5.1 // indirect
|
github.com/bufbuild/connect-opentelemetry-go v0.4.0 // indirect
|
||||||
|
github.com/bufbuild/protocompile v0.6.0 // indirect
|
||||||
|
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
github.com/cristalhq/acmd v0.11.1 // indirect
|
github.com/cristalhq/acmd v0.11.1 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/docker/cli v23.0.1+incompatible // indirect
|
github.com/docker/cli v24.0.5+incompatible // indirect
|
||||||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
github.com/docker/distribution v2.8.2+incompatible // indirect
|
||||||
github.com/docker/docker v23.0.3+incompatible // indirect
|
github.com/docker/docker v24.0.5+incompatible // indirect
|
||||||
github.com/docker/docker-credential-helpers v0.7.0 // indirect
|
github.com/docker/docker-credential-helpers v0.8.0 // indirect
|
||||||
github.com/docker/go-connections v0.4.0 // indirect
|
github.com/docker/go-connections v0.4.0 // indirect
|
||||||
github.com/docker/go-units v0.5.0 // indirect
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
github.com/felixge/fgprof v0.9.3 // indirect
|
github.com/felixge/fgprof v0.9.3 // indirect
|
||||||
github.com/go-chi/chi/v5 v5.0.8 // indirect
|
github.com/go-chi/chi/v5 v5.0.10 // indirect
|
||||||
github.com/go-logr/logr v1.2.3 // indirect
|
github.com/go-logr/logr v1.2.4 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-toolsmith/astcast v1.1.0 // indirect
|
github.com/go-toolsmith/astcast v1.1.0 // indirect
|
||||||
github.com/go-toolsmith/astcopy v1.1.0 // indirect
|
github.com/go-toolsmith/astcopy v1.1.0 // indirect
|
||||||
|
|
@ -47,25 +51,24 @@ require (
|
||||||
github.com/go-toolsmith/pkgload v1.2.2 // indirect
|
github.com/go-toolsmith/pkgload v1.2.2 // indirect
|
||||||
github.com/go-toolsmith/strparse v1.1.0 // indirect
|
github.com/go-toolsmith/strparse v1.1.0 // indirect
|
||||||
github.com/go-toolsmith/typep v1.1.0 // indirect
|
github.com/go-toolsmith/typep v1.1.0 // indirect
|
||||||
github.com/gofrs/flock v0.8.1 // indirect
|
|
||||||
github.com/gofrs/uuid/v5 v5.0.0 // indirect
|
github.com/gofrs/uuid/v5 v5.0.0 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/glog v1.0.0 // indirect
|
github.com/golang/glog v1.1.2 // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/google/go-containerregistry v0.13.0 // indirect
|
github.com/google/go-containerregistry v0.16.1 // indirect
|
||||||
github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 // indirect
|
github.com/google/pprof v0.0.0-20230808223545-4887780b67fb // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jdxcode/netrc v0.0.0-20221124155335-4616370d1a84 // indirect
|
github.com/jdxcode/netrc v0.0.0-20221124155335-4616370d1a84 // indirect
|
||||||
github.com/klauspost/compress v1.16.0 // indirect
|
github.com/klauspost/compress v1.16.7 // indirect
|
||||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
|
github.com/moby/term v0.5.0 // indirect
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.1.0-rc2 // indirect
|
github.com/opencontainers/image-spec v1.1.0-rc4 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
|
@ -75,29 +78,33 @@ require (
|
||||||
github.com/quasilyte/gogrep v0.5.0 // indirect
|
github.com/quasilyte/gogrep v0.5.0 // indirect
|
||||||
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect
|
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect
|
||||||
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
|
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
|
||||||
github.com/rs/cors v1.8.3 // indirect
|
github.com/rs/cors v1.9.0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/spf13/afero v1.9.3 // indirect
|
github.com/spf13/afero v1.9.3 // indirect
|
||||||
github.com/spf13/cast v1.5.0 // indirect
|
github.com/spf13/cast v1.5.0 // indirect
|
||||||
github.com/spf13/cobra v1.6.1 // indirect
|
github.com/spf13/cobra v1.7.0 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/subosito/gotenv v1.4.2 // indirect
|
github.com/subosito/gotenv v1.4.2 // indirect
|
||||||
go.opentelemetry.io/otel v1.14.0 // indirect
|
github.com/tetratelabs/wazero v1.4.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk v1.14.0 // indirect
|
github.com/vbatts/tar-split v0.11.5 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.14.0 // indirect
|
go.opentelemetry.io/otel v1.16.0 // indirect
|
||||||
go.uber.org/atomic v1.10.0 // indirect
|
go.opentelemetry.io/otel/metric v1.16.0 // indirect
|
||||||
go.uber.org/multierr v1.10.0 // indirect
|
go.opentelemetry.io/otel/sdk v1.16.0 // indirect
|
||||||
go.uber.org/zap v1.24.0 // indirect
|
go.opentelemetry.io/otel/trace v1.16.0 // indirect
|
||||||
golang.org/x/crypto v0.7.0 // indirect
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
golang.org/x/exp/typeparams v0.0.0-20230213192124-5e25df0256eb // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/mod v0.9.0 // indirect
|
go.uber.org/zap v1.25.0 // indirect
|
||||||
golang.org/x/net v0.8.0 // indirect
|
golang.org/x/crypto v0.12.0 // indirect
|
||||||
golang.org/x/sync v0.1.0 // indirect
|
golang.org/x/exp/typeparams v0.0.0-20230809150735-7b3493d9a819 // indirect
|
||||||
golang.org/x/sys v0.6.0 // indirect
|
golang.org/x/mod v0.12.0 // indirect
|
||||||
golang.org/x/term v0.6.0 // indirect
|
golang.org/x/net v0.14.0 // indirect
|
||||||
golang.org/x/text v0.8.0 // indirect
|
golang.org/x/sync v0.3.0 // indirect
|
||||||
golang.org/x/tools v0.7.0 // indirect
|
golang.org/x/sys v0.11.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20230223222841-637eb2293923 // indirect
|
golang.org/x/term v0.11.0 // indirect
|
||||||
|
golang.org/x/text v0.12.0 // indirect
|
||||||
|
golang.org/x/tools v0.12.0 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20230807174057-1744710a1577 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
193
go.sum
193
go.sum
|
|
@ -40,15 +40,17 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
|
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||||
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
||||||
github.com/bufbuild/buf v1.15.1 h1:v7sK2uMEsGX4Z2hvu+xiMheH3C3AKBGfxPBgdUZYDQ8=
|
github.com/bufbuild/buf v1.26.0 h1:rB8pErEkxdUNhVZ3+ClFEoJRaa+o2FjEecCWL2S3Qs4=
|
||||||
github.com/bufbuild/buf v1.15.1/go.mod h1:TQeGKam1QMfHy/xsSnnMpxN3JK5HBb6aNvZj4m52gkE=
|
github.com/bufbuild/buf v1.26.0/go.mod h1:UMPncXMWgrmIM+0QpwTEwjNr2SA0z2YIVZZsmNflvB4=
|
||||||
github.com/bufbuild/connect-go v1.5.2 h1:G4EZd5gF1U1ZhhbVJXplbuUnfKpBZ5j5izqIwu2g2W8=
|
github.com/bufbuild/connect-go v1.10.0 h1:QAJ3G9A1OYQW2Jbk3DeoJbkCxuKArrvZgDt47mjdTbg=
|
||||||
github.com/bufbuild/connect-go v1.5.2/go.mod h1:GmMJYR6orFqD0Y6ZgX8pwQ8j9baizDrIQMm1/a6LnHk=
|
github.com/bufbuild/connect-go v1.10.0/go.mod h1:CAIePUgkDR5pAFaylSMtNK45ANQjp9JvpluG20rhpV8=
|
||||||
github.com/bufbuild/protocompile v0.5.1 h1:mixz5lJX4Hiz4FpqFREJHIXLfaLBntfaJv1h+/jS+Qg=
|
github.com/bufbuild/connect-opentelemetry-go v0.4.0 h1:6JAn10SNqlQ/URhvRNGrIlczKw1wEXknBUUtmWqOiak=
|
||||||
github.com/bufbuild/protocompile v0.5.1/go.mod h1:G5iLmavmF4NsYtpZFvE3B/zFch2GIY8+wjsYLR/lc40=
|
github.com/bufbuild/connect-opentelemetry-go v0.4.0/go.mod h1:nwPXYoDOoc2DGyKE/6pT1Q9MPSi2Et2e6BieMD0l6WU=
|
||||||
|
github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY=
|
||||||
|
github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
|
@ -57,7 +59,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/containerd/stargz-snapshotter/estargz v0.12.1 h1:+7nYmHJb0tEkcRaAW+MHqoKaJYZmkikupxCqVtmPuY0=
|
github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
|
||||||
|
github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||||
|
|
@ -66,14 +69,14 @@ github.com/cristalhq/acmd v0.11.1/go.mod h1:LG5oa43pE/BbxtfMoImHCQN++0Su7dzipdgB
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/docker/cli v23.0.1+incompatible h1:LRyWITpGzl2C9e9uGxzisptnxAn1zfZKXy13Ul2Q5oM=
|
github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc=
|
||||||
github.com/docker/cli v23.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
github.com/docker/cli v24.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||||
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
|
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
||||||
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/docker v23.0.3+incompatible h1:9GhVsShNWz1hO//9BNg/dpMnZW25KydO4wtVxWAIbho=
|
github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
|
||||||
github.com/docker/docker v23.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
|
github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8=
|
||||||
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
|
github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40=
|
||||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
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-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
|
|
@ -91,16 +94,16 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4
|
||||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||||
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
|
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
|
||||||
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
|
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
|
||||||
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
|
||||||
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/go-critic/go-critic v0.7.0 h1:tqbKzB8pqi0NsRZ+1pyU4aweAF7A7QN0Pi4Q02+rYnQ=
|
github.com/go-critic/go-critic v0.8.2 h1:mekhZ9jw5NBEj3I8o/EywXw5zBfGAJuMo4VVVjtxF80=
|
||||||
github.com/go-critic/go-critic v0.7.0/go.mod h1:moYzd7GdVXE2C2hYTwd7h0CPcqlUeclsyBRwMa38v64=
|
github.com/go-critic/go-critic v0.8.2/go.mod h1:nZPlrtVfOuLOe8GpvWTfcMzfkG0QVZWAziAeXpivfQo=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/go-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8=
|
github.com/go-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8=
|
||||||
|
|
@ -122,7 +125,6 @@ github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJ
|
||||||
github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus=
|
github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus=
|
||||||
github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig=
|
github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig=
|
||||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
|
||||||
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
|
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
|
||||||
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
|
|
@ -130,8 +132,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
|
github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
|
||||||
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
|
github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
|
@ -173,8 +175,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-containerregistry v0.13.0 h1:y1C7Z3e149OJbOPDBxLYR8ITPz8dTKqQwjErKVHJC8k=
|
github.com/google/go-containerregistry v0.16.1 h1:rUEt426sR6nyrL3gt+18ibRcvYpKYdpsa5ZW7MA08dQ=
|
||||||
github.com/google/go-containerregistry v0.13.0/go.mod h1:J9FQ+eSS4a1aC2GNZxvNpbWhgp0487v+cgiilB4FqDo=
|
github.com/google/go-containerregistry v0.16.1/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
|
|
@ -189,8 +191,8 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
|
||||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
|
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
|
||||||
github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 h1:CqYfpuYIjnlNxM3msdyPRKabhXZWbKjf3Q8BWROFBso=
|
github.com/google/pprof v0.0.0-20230808223545-4887780b67fb h1:oqpb3Cwpc7EOml5PVGMYbSGmwNui2R7i8IW83gs4W0c=
|
||||||
github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
|
github.com/google/pprof v0.0.0-20230808223545-4887780b67fb/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
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/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
|
|
@ -198,8 +200,10 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
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/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 h1:gDLXvp5S9izjldquuoAhDzccbskOL6tDC5jMSyx3zxE=
|
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2/go.mod h1:7pdNwVWBBHGiCxa9lAszqCJMbfTISJ7oMftp8+UGV08=
|
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.2 h1:dygLcbEBA+t/P7ck6a8AkXv6juQ4cK0RHBoh32jxhHM=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.2/go.mod h1:Ap9RLCIJVtgQg1/BBgVEfypOAySvvlcpcVQkSzJCH4Y=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
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/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
|
|
@ -207,7 +211,6 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/jdxcode/netrc v0.0.0-20221124155335-4616370d1a84 h1:2uT3aivO7NVpUPGcQX7RbHijHMyWix/yCnIrCWc+5co=
|
github.com/jdxcode/netrc v0.0.0-20221124155335-4616370d1a84 h1:2uT3aivO7NVpUPGcQX7RbHijHMyWix/yCnIrCWc+5co=
|
||||||
|
|
@ -217,10 +220,10 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
|
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
|
||||||
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||||
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
|
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
|
@ -233,15 +236,15 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
|
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
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/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034=
|
github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0=
|
||||||
github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
|
github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
|
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
||||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||||
|
|
@ -266,18 +269,18 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||||
github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo=
|
github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE=
|
||||||
github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
|
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
|
||||||
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
|
@ -294,11 +297,14 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
||||||
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||||
github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME=
|
github.com/tetratelabs/wazero v1.4.0 h1:9/MirYvmkJ/zSUOygKY/ia3t+e+RqIZXKbylIby1WYk=
|
||||||
|
github.com/tetratelabs/wazero v1.4.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A=
|
||||||
|
github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts=
|
||||||
|
github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
|
@ -309,19 +315,22 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||||
go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
|
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
|
||||||
go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
|
go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
|
||||||
go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY=
|
go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo=
|
||||||
go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM=
|
go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4=
|
||||||
go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
|
go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE=
|
||||||
go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
|
go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4=
|
||||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI=
|
||||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
|
||||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
|
||||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
|
go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
|
||||||
|
go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-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-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
|
@ -329,8 +338,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-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-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
|
@ -341,12 +350,12 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
golang.org/x/exp v0.0.0-20230307190834-24139beb5833 h1:SChBja7BCQewoTAU7IgvucQKMIXrEpFxNMs0spT3/5s=
|
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw=
|
||||||
golang.org/x/exp v0.0.0-20230307190834-24139beb5833/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||||
golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||||
golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||||
golang.org/x/exp/typeparams v0.0.0-20230213192124-5e25df0256eb h1:WGs/bGIWYyAY5PVgGGMXqGGCxSJz4fpoUExb/vgqNCU=
|
golang.org/x/exp/typeparams v0.0.0-20230809150735-7b3493d9a819 h1:qdPQGUfaPzFgEbhQCulaXk+s0ETcox/vQd+f9YCIuxY=
|
||||||
golang.org/x/exp/typeparams v0.0.0-20230213192124-5e25df0256eb/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
golang.org/x/exp/typeparams v0.0.0-20230809150735-7b3493d9a819/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
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/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
|
@ -370,8 +379,8 @@ 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/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
|
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
|
@ -403,8 +412,8 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
|
@ -424,8 +433,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
|
@ -465,19 +474,19 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
|
||||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.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.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.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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
|
@ -531,8 +540,8 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
|
||||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|
@ -599,8 +608,12 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
|
||||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20230223222841-637eb2293923 h1:znp6mq/drrY+6khTAlJUDNFFcDGV2ENLYKpMq8SyCds=
|
google.golang.org/genproto v0.0.0-20230807174057-1744710a1577 h1:Tyk/35yqszRCvaragTn5NnkY6IiKk/XvHzEWepo71N0=
|
||||||
google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw=
|
google.golang.org/genproto v0.0.0-20230807174057-1744710a1577/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20230807174057-1744710a1577 h1:xv8KoglAClYGkprUSmDTKaILtzfD8XzG9NYVXMprjKo=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20230807174057-1744710a1577/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 h1:wukfNtZmZUurLN/atp2hiIeTKn7QJWIQdHzqmsOnAOk=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
|
@ -617,8 +630,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
|
||||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
|
google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
|
||||||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA=
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA=
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y=
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
|
@ -633,8 +646,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
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-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
cypress:
|
cypress:
|
||||||
npm install
|
npm install
|
||||||
./cypressRun.sh "general"
|
./cypressRun.sh "general"
|
||||||
./cypressRun.sh "hiddenNav"
|
./cypressRun.sh "hiddenNav"
|
||||||
|
|
||||||
.PHONY: cypress container
|
.PHONY: cypress container
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
# OliveTin-integration-tests
|
# OliveTin-integration-tests
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# (eg, snapshot builds on GitHub)
|
# (eg, snapshot builds on GitHub)
|
||||||
|
|
||||||
|
|
||||||
Vagrant.configure("2") do |config|
|
Vagrant.configure("2") do |config|
|
||||||
config.vm.box = "generic/centos8"
|
config.vm.box = "generic/centos8"
|
||||||
config.vm.provision "shell", inline: "mkdir /etc/OliveTin && chmod o+w /etc/OliveTin/", privileged: true
|
config.vm.provision "shell", inline: "mkdir /etc/OliveTin && chmod o+w /etc/OliveTin/", privileged: true
|
||||||
config.vm.provision "file", source: "configs/config.general.yaml/.", destination: "/etc/OliveTin/config.yaml"
|
config.vm.provision "file", source: "configs/config.general.yaml/.", destination: "/etc/OliveTin/config.yaml"
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
#
|
#
|
||||||
# Integration Test Config: General
|
# Integration Test Config: General
|
||||||
#
|
#
|
||||||
|
|
||||||
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
|
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
|
||||||
|
|
||||||
logLevel: "DEBUG"
|
logLevel: "DEBUG"
|
||||||
checkForUpdates: false
|
checkForUpdates: false
|
||||||
|
|
||||||
actions:
|
actions:
|
||||||
- title: Ping Google.com
|
- title: Ping Google.com
|
||||||
shell: ping google.com -c 1
|
shell: ping google.com -c 1
|
||||||
icon: ping
|
icon: ping
|
||||||
|
|
||||||
- title: restart lightdm
|
- title: restart lightdm
|
||||||
icon: poop
|
icon: poop
|
||||||
shell: ssh root@overseer 'service lightdm restart'
|
shell: ssh root@overseer 'service lightdm restart'
|
||||||
|
|
@ -32,4 +32,3 @@ actions:
|
||||||
- title: Restart Plex
|
- title: Restart Plex
|
||||||
icon: smile
|
icon: smile
|
||||||
shell: docker restart plex
|
shell: docker restart plex
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
#
|
#
|
||||||
# Integration Test Config: General
|
# Integration Test Config: General
|
||||||
#
|
#
|
||||||
|
|
||||||
showFooter: false
|
showFooter: false
|
||||||
checkForUpdates: false
|
checkForUpdates: false
|
||||||
|
|
||||||
# Actions (buttons) to show up on the WebUI:
|
# Actions (buttons) to show up on the WebUI:
|
||||||
actions:
|
actions:
|
||||||
- title: Ping example.com
|
- title: Ping example.com
|
||||||
shell: ping example.com -c 1
|
shell: ping example.com -c 1
|
||||||
icon: ping
|
icon: ping
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
#
|
#
|
||||||
# Integration Test Config: General
|
# Integration Test Config: General
|
||||||
#
|
#
|
||||||
|
|
||||||
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
|
listenAddressSingleHTTPFrontend: 0.0.0.0:1337
|
||||||
|
|
||||||
showNavigation: false
|
showNavigation: false
|
||||||
checkForUpdates: false
|
checkForUpdates: false
|
||||||
|
|
||||||
# Actions (buttons) to show up on the WebUI:
|
# Actions (buttons) to show up on the WebUI:
|
||||||
actions:
|
actions:
|
||||||
- title: Ping example.com
|
- title: Ping example.com
|
||||||
shell: ping example.com -c 1
|
shell: ping example.com -c 1
|
||||||
icon: ping
|
icon: ping
|
||||||
|
|
|
||||||
|
|
@ -17,5 +17,3 @@ describe('Homepage rendering', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,3 @@ describe('Hidden Footer', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,5 +14,3 @@ describe('Hidden Nav', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,4 +23,3 @@
|
||||||
//
|
//
|
||||||
// -- This will overwrite an existing command --
|
// -- This will overwrite an existing command --
|
||||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,3 @@ cp -f ./configs/config.$1.yaml ./configs/config.yaml
|
||||||
docker start olivetin
|
docker start olivetin
|
||||||
NO_COLOR=1 ./node_modules/.bin/cypress run --headless -s cypress/integration/$1/* || true
|
NO_COLOR=1 ./node_modules/.bin/cypress run --headless -s cypress/integration/$1/* || true
|
||||||
docker kill olivetin
|
docker kill olivetin
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -75,7 +75,10 @@ func getMetdataKeyOrEmpty(md metadata.MD, key string) string {
|
||||||
func UserFromContext(ctx context.Context, cfg *config.Config) *AuthenticatedUser {
|
func UserFromContext(ctx context.Context, cfg *config.Config) *AuthenticatedUser {
|
||||||
md, ok := metadata.FromIncomingContext(ctx)
|
md, ok := metadata.FromIncomingContext(ctx)
|
||||||
|
|
||||||
ret := &AuthenticatedUser{}
|
ret := &AuthenticatedUser{
|
||||||
|
Username: "guest",
|
||||||
|
Usergroup: "guest",
|
||||||
|
}
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
ret.Username = getMetdataKeyOrEmpty(md, "username")
|
ret.Username = getMetdataKeyOrEmpty(md, "username")
|
||||||
|
|
@ -87,7 +90,7 @@ func UserFromContext(ctx context.Context, cfg *config.Config) *AuthenticatedUser
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"username": ret.Username,
|
"username": ret.Username,
|
||||||
"usergroup": ret.Usergroup,
|
"usergroup": ret.Usergroup,
|
||||||
}).Infof("UserFromContext")
|
}).Debugf("UserFromContext")
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,9 @@ type Action struct {
|
||||||
Acls []string
|
Acls []string
|
||||||
ExecOnStartup bool
|
ExecOnStartup bool
|
||||||
ExecOnCron []string
|
ExecOnCron []string
|
||||||
|
MaxConcurrent int
|
||||||
Arguments []ActionArgument
|
Arguments []ActionArgument
|
||||||
|
PopupOnStart bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionArgument objects appear on Actions.
|
// ActionArgument objects appear on Actions.
|
||||||
|
|
@ -73,9 +75,10 @@ type Config struct {
|
||||||
ShowNavigation bool
|
ShowNavigation bool
|
||||||
ShowNewVersions bool
|
ShowNewVersions bool
|
||||||
AuthJwtCookieName string
|
AuthJwtCookieName string
|
||||||
AuthJwtSecret string
|
AuthJwtSecret string // mutually exclusive with pub key config fields
|
||||||
AuthJwtClaimUsername string
|
AuthJwtClaimUsername string
|
||||||
AuthJwtClaimUserGroup string
|
AuthJwtClaimUserGroup string
|
||||||
|
AuthJwtPubKeyPath string // will read pub key from file on disk
|
||||||
AuthHttpHeaderUsername string
|
AuthHttpHeaderUsername string
|
||||||
AuthHttpHeaderUserGroup string
|
AuthHttpHeaderUserGroup string
|
||||||
DefaultPermissions PermissionsList
|
DefaultPermissions PermissionsList
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,10 @@ func (action *Action) sanitize() {
|
||||||
|
|
||||||
action.Icon = lookupHTMLIcon(action.Icon)
|
action.Icon = lookupHTMLIcon(action.Icon)
|
||||||
|
|
||||||
|
if action.MaxConcurrent < 1 {
|
||||||
|
action.MaxConcurrent = 1
|
||||||
|
}
|
||||||
|
|
||||||
for idx := range action.Arguments {
|
for idx := range action.Arguments {
|
||||||
action.Arguments[idx].sanitize()
|
action.Arguments[idx].sanitize()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,9 @@ var (
|
||||||
|
|
||||||
func parseActionArguments(rawShellCommand string, values map[string]string, action *config.Action) (string, error) {
|
func parseActionArguments(rawShellCommand string, values map[string]string, action *config.Action) (string, error) {
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"cmd": rawShellCommand,
|
"actionTitle": action.Title,
|
||||||
}).Infof("Before Parse Args")
|
"cmd": rawShellCommand,
|
||||||
|
}).Infof("Action parse args - Before")
|
||||||
|
|
||||||
r := regexp.MustCompile("{{ *?([a-zA-Z0-9_]+?) *?}}")
|
r := regexp.MustCompile("{{ *?([a-zA-Z0-9_]+?) *?}}")
|
||||||
matches := r.FindAllStringSubmatch(rawShellCommand, -1)
|
matches := r.FindAllStringSubmatch(rawShellCommand, -1)
|
||||||
|
|
@ -51,8 +52,9 @@ func parseActionArguments(rawShellCommand string, values map[string]string, acti
|
||||||
}
|
}
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"cmd": rawShellCommand,
|
"actionTitle": action.Title,
|
||||||
}).Infof("After Parse Args")
|
"cmd": rawShellCommand,
|
||||||
|
}).Infof("Action parse args - After")
|
||||||
|
|
||||||
return rawShellCommand, nil
|
return rawShellCommand, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,91 +8,77 @@ import (
|
||||||
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Executor represents a helper class for executing commands. It's main method
|
||||||
|
// is ExecRequest
|
||||||
|
type Executor struct {
|
||||||
|
Logs map[string]*InternalLogEntry
|
||||||
|
|
||||||
|
listeners []listener
|
||||||
|
|
||||||
|
chainOfCommand []executorStepFunc
|
||||||
|
}
|
||||||
|
|
||||||
// ExecutionRequest is a request to execute an action. It's passed to an
|
// ExecutionRequest is a request to execute an action. It's passed to an
|
||||||
// Executor. They're created from the grpcapi.
|
// Executor. They're created from the grpcapi.
|
||||||
type ExecutionRequest struct {
|
type ExecutionRequest struct {
|
||||||
ActionName string
|
ActionName string
|
||||||
Arguments map[string]string
|
Arguments map[string]string
|
||||||
|
UUID string
|
||||||
Tags []string
|
Tags []string
|
||||||
action *config.Action
|
action *config.Action
|
||||||
Cfg *config.Config
|
Cfg *config.Config
|
||||||
AuthenticatedUser *acl.AuthenticatedUser
|
AuthenticatedUser *acl.AuthenticatedUser
|
||||||
logEntry *InternalLogEntry
|
logEntry *InternalLogEntry
|
||||||
finalParsedCommand string
|
finalParsedCommand string
|
||||||
|
executor *Executor
|
||||||
}
|
}
|
||||||
|
|
||||||
// InternalLogEntry objects are created by an Executor, and represent the final
|
// InternalLogEntry objects are created by an Executor, and represent the final
|
||||||
// state of execution (even if the command is not executed). It's designed to be
|
// state of execution (even if the command is not executed). It's designed to be
|
||||||
// easily serializable.
|
// easily serializable.
|
||||||
type InternalLogEntry struct {
|
type InternalLogEntry struct {
|
||||||
Datetime string
|
DatetimeStarted string
|
||||||
Stdout string
|
DatetimeFinished string
|
||||||
Stderr string
|
Stdout string
|
||||||
TimedOut bool
|
Stderr string
|
||||||
ExitCode int32
|
StdoutBuffer io.ReadCloser
|
||||||
Tags []string
|
StderrBuffer io.ReadCloser
|
||||||
|
TimedOut bool
|
||||||
|
Blocked bool
|
||||||
|
ExitCode int32
|
||||||
|
Tags []string
|
||||||
|
ExecutionStarted bool
|
||||||
|
ExecutionFinished bool
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The following two properties are obviously on Action normally, but it's useful
|
The following 3 properties are obviously on Action normally, but it's useful
|
||||||
that logs are lightweight (so we don't need to have an action associated to
|
that logs are lightweight (so we don't need to have an action associated to
|
||||||
logs, etc. Therefore, we duplicate those values here.
|
logs, etc. Therefore, we duplicate those values here.
|
||||||
*/
|
*/
|
||||||
ActionTitle string
|
ActionTitle string
|
||||||
ActionIcon string
|
ActionIcon string
|
||||||
|
UUID string
|
||||||
}
|
}
|
||||||
|
|
||||||
type executorStepFunc func(*ExecutionRequest) bool
|
type executorStepFunc func(*ExecutionRequest) bool
|
||||||
|
|
||||||
// Executor represents a helper class for executing commands. It's main method
|
|
||||||
// is ExecRequest
|
|
||||||
type Executor struct {
|
|
||||||
Logs []InternalLogEntry
|
|
||||||
|
|
||||||
chainOfCommand []executorStepFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecRequest processes an ExecutionRequest
|
|
||||||
func (e *Executor) ExecRequest(req *ExecutionRequest) *pb.StartActionResponse {
|
|
||||||
req.logEntry = &InternalLogEntry{
|
|
||||||
Datetime: time.Now().Format("2006-01-02 15:04:05"),
|
|
||||||
ActionTitle: req.ActionName,
|
|
||||||
Stdout: "",
|
|
||||||
Stderr: "",
|
|
||||||
ExitCode: -1337, // If an Action is not actually executed, this is the default exit code.
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, step := range e.chainOfCommand {
|
|
||||||
if !step(req) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Logs = append(e.Logs, *req.logEntry)
|
|
||||||
|
|
||||||
return &pb.StartActionResponse{
|
|
||||||
LogEntry: &pb.LogEntry{
|
|
||||||
ActionTitle: req.logEntry.ActionTitle,
|
|
||||||
ActionIcon: req.logEntry.ActionIcon,
|
|
||||||
Datetime: req.logEntry.Datetime,
|
|
||||||
Stderr: req.logEntry.Stderr,
|
|
||||||
Stdout: req.logEntry.Stdout,
|
|
||||||
TimedOut: req.logEntry.TimedOut,
|
|
||||||
ExitCode: req.logEntry.ExitCode,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultExecutor returns an Executor, with a sensible "chain of command" for
|
// DefaultExecutor returns an Executor, with a sensible "chain of command" for
|
||||||
// executing actions.
|
// executing actions.
|
||||||
func DefaultExecutor() *Executor {
|
func DefaultExecutor() *Executor {
|
||||||
e := Executor{}
|
e := Executor{}
|
||||||
|
e.Logs = make(map[string]*InternalLogEntry)
|
||||||
|
|
||||||
e.chainOfCommand = []executorStepFunc{
|
e.chainOfCommand = []executorStepFunc{
|
||||||
|
stepLogRequested,
|
||||||
stepFindAction,
|
stepFindAction,
|
||||||
|
stepConcurrencyCheck,
|
||||||
stepACLCheck,
|
stepACLCheck,
|
||||||
stepParseArgs,
|
stepParseArgs,
|
||||||
stepLogStart,
|
stepLogStart,
|
||||||
|
|
@ -103,6 +89,88 @@ func DefaultExecutor() *Executor {
|
||||||
return &e
|
return &e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type listener interface {
|
||||||
|
OnExecutionStarted(actionName string)
|
||||||
|
OnExecutionFinished(logEntry *InternalLogEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Executor) AddListener(m listener) {
|
||||||
|
e.listeners = append(e.listeners, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecRequest processes an ExecutionRequest
|
||||||
|
func (e *Executor) ExecRequest(req *ExecutionRequest) *pb.StartActionResponse {
|
||||||
|
// req.UUID is now set by the client, so that they can track the request
|
||||||
|
// from start to finish. This means that a malicious client could send
|
||||||
|
// duplicate UUIDs (or just random strings), but this is the only way.
|
||||||
|
req.executor = e
|
||||||
|
req.logEntry = &InternalLogEntry{
|
||||||
|
DatetimeStarted: time.Now().Format("2006-01-02 15:04:05"),
|
||||||
|
ActionTitle: req.ActionName,
|
||||||
|
UUID: req.UUID,
|
||||||
|
Stdout: "",
|
||||||
|
Stderr: "",
|
||||||
|
ExitCode: -1337, // If an Action is not actually executed, this is the default exit code.
|
||||||
|
ExecutionStarted: false,
|
||||||
|
ExecutionFinished: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Logs[req.UUID] = req.logEntry
|
||||||
|
|
||||||
|
for _, listener := range e.listeners {
|
||||||
|
listener.OnExecutionStarted(req.ActionName)
|
||||||
|
}
|
||||||
|
|
||||||
|
go e.execChain(req)
|
||||||
|
|
||||||
|
return &pb.StartActionResponse{
|
||||||
|
ExecutionUuid: req.UUID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Executor) execChain(req *ExecutionRequest) {
|
||||||
|
for _, step := range e.chainOfCommand {
|
||||||
|
if !step(req) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req.logEntry.ExecutionFinished = true
|
||||||
|
|
||||||
|
// This isn't a step, because we want to notify all listeners, irrespective
|
||||||
|
// of how many steps were actually executed.
|
||||||
|
notifyListeners(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConcurrentCount(req *ExecutionRequest) int {
|
||||||
|
concurrentCount := 0
|
||||||
|
|
||||||
|
for _, log := range req.executor.Logs {
|
||||||
|
if log.ActionTitle == req.ActionName && !log.ExecutionFinished {
|
||||||
|
concurrentCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return concurrentCount
|
||||||
|
}
|
||||||
|
|
||||||
|
func stepConcurrencyCheck(req *ExecutionRequest) bool {
|
||||||
|
concurrentCount := getConcurrentCount(req)
|
||||||
|
|
||||||
|
// Note that the current execution is counted int the logs, so when checking we +1
|
||||||
|
if concurrentCount >= (req.action.MaxConcurrent + 1) {
|
||||||
|
msg := fmt.Sprintf("Blocked from executing. This would mean this action is running %d times concurrently, but this action has maxExecutions set to %d.", concurrentCount, req.action.MaxConcurrent)
|
||||||
|
|
||||||
|
log.Warnf(msg)
|
||||||
|
|
||||||
|
req.logEntry.Stdout = msg
|
||||||
|
req.logEntry.Blocked = true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func stepFindAction(req *ExecutionRequest) bool {
|
func stepFindAction(req *ExecutionRequest) bool {
|
||||||
actualAction := req.Cfg.FindAction(req.ActionName)
|
actualAction := req.Cfg.FindAction(req.ActionName)
|
||||||
|
|
||||||
|
|
@ -142,10 +210,18 @@ func stepParseArgs(req *ExecutionRequest) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func stepLogRequested(req *ExecutionRequest) bool {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"actionTitle": req.ActionName,
|
||||||
|
}).Infof("Action requested")
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func stepLogStart(req *ExecutionRequest) bool {
|
func stepLogStart(req *ExecutionRequest) bool {
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"title": req.action.Title,
|
"actionTitle": req.action.Title,
|
||||||
"timeout": req.action.Timeout,
|
"timeout": req.action.Timeout,
|
||||||
}).Infof("Action starting")
|
}).Infof("Action starting")
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
@ -153,16 +229,22 @@ func stepLogStart(req *ExecutionRequest) bool {
|
||||||
|
|
||||||
func stepLogFinish(req *ExecutionRequest) bool {
|
func stepLogFinish(req *ExecutionRequest) bool {
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"title": req.action.Title,
|
"actionTitle": req.action.Title,
|
||||||
"stdout": req.logEntry.Stdout,
|
"stdout": req.logEntry.Stdout,
|
||||||
"stderr": req.logEntry.Stderr,
|
"stderr": req.logEntry.Stderr,
|
||||||
"timedOut": req.logEntry.TimedOut,
|
"timedOut": req.logEntry.TimedOut,
|
||||||
"exit": req.logEntry.ExitCode,
|
"exit": req.logEntry.ExitCode,
|
||||||
}).Infof("Action finished")
|
}).Infof("Action finished")
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func notifyListeners(req *ExecutionRequest) {
|
||||||
|
for _, listener := range req.executor.listeners {
|
||||||
|
listener.OnExecutionFinished(req.logEntry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func wrapCommandInShell(ctx context.Context, finalParsedCommand string) *exec.Cmd {
|
func wrapCommandInShell(ctx context.Context, finalParsedCommand string) *exec.Cmd {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
return exec.CommandContext(ctx, "cmd", "/C", finalParsedCommand)
|
return exec.CommandContext(ctx, "cmd", "/C", finalParsedCommand)
|
||||||
|
|
@ -181,8 +263,17 @@ func stepExec(req *ExecutionRequest) bool {
|
||||||
cmd := wrapCommandInShell(ctx, req.finalParsedCommand)
|
cmd := wrapCommandInShell(ctx, req.finalParsedCommand)
|
||||||
cmd.Stdout = &stdout
|
cmd.Stdout = &stdout
|
||||||
cmd.Stderr = &stderr
|
cmd.Stderr = &stderr
|
||||||
|
req.logEntry.StdoutBuffer, _ = cmd.StdoutPipe()
|
||||||
|
req.logEntry.StderrBuffer, _ = cmd.StderrPipe()
|
||||||
|
|
||||||
runerr := cmd.Run()
|
req.logEntry.ExecutionStarted = true
|
||||||
|
|
||||||
|
runerr := cmd.Start()
|
||||||
|
|
||||||
|
cmd.Wait()
|
||||||
|
|
||||||
|
// req.logEntry.Stdout = req.logEntry.StdoutBuffer.String()
|
||||||
|
// req.logEntry.Stderr = req.logEntry.StderrBuffer.String()
|
||||||
|
|
||||||
req.logEntry.ExitCode = int32(cmd.ProcessState.ExitCode())
|
req.logEntry.ExitCode = int32(cmd.ProcessState.ExitCode())
|
||||||
req.logEntry.Stdout = stdout.String()
|
req.logEntry.Stdout = stdout.String()
|
||||||
|
|
@ -197,6 +288,7 @@ func stepExec(req *ExecutionRequest) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
req.logEntry.Tags = req.Tags
|
req.logEntry.Tags = req.Tags
|
||||||
|
req.logEntry.DatetimeFinished = time.Now().Format("2006-01-02 15:04:05")
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"net"
|
"net"
|
||||||
|
"sort"
|
||||||
|
|
||||||
acl "github.com/OliveTin/OliveTin/internal/acl"
|
acl "github.com/OliveTin/OliveTin/internal/acl"
|
||||||
config "github.com/OliveTin/OliveTin/internal/config"
|
config "github.com/OliveTin/OliveTin/internal/config"
|
||||||
|
|
@ -19,7 +20,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type oliveTinAPI struct {
|
type oliveTinAPI struct {
|
||||||
pb.UnimplementedOliveTinApiServer
|
pb.UnimplementedOliveTinApiServiceServer
|
||||||
|
|
||||||
executor *executor.Executor
|
executor *executor.Executor
|
||||||
}
|
}
|
||||||
|
|
@ -35,6 +36,7 @@ func (api *oliveTinAPI) StartAction(ctx ctx.Context, req *pb.StartActionRequest)
|
||||||
|
|
||||||
execReq := executor.ExecutionRequest{
|
execReq := executor.ExecutionRequest{
|
||||||
ActionName: req.ActionName,
|
ActionName: req.ActionName,
|
||||||
|
UUID: req.Uuid,
|
||||||
Arguments: args,
|
Arguments: args,
|
||||||
AuthenticatedUser: acl.UserFromContext(ctx, cfg),
|
AuthenticatedUser: acl.UserFromContext(ctx, cfg),
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
|
|
@ -43,6 +45,62 @@ func (api *oliveTinAPI) StartAction(ctx ctx.Context, req *pb.StartActionRequest)
|
||||||
return api.executor.ExecRequest(&execReq), nil
|
return api.executor.ExecRequest(&execReq), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *oliveTinAPI) ExecutionStatus(ctx ctx.Context, req *pb.ExecutionStatusRequest) (*pb.ExecutionStatusResponse, error) {
|
||||||
|
res := &pb.ExecutionStatusResponse{}
|
||||||
|
|
||||||
|
logEntry, ok := api.executor.Logs[req.ExecutionUuid]
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
res.LogEntry = &pb.LogEntry{
|
||||||
|
ActionTitle: logEntry.ActionTitle,
|
||||||
|
ActionIcon: logEntry.ActionIcon,
|
||||||
|
DatetimeStarted: logEntry.DatetimeStarted,
|
||||||
|
DatetimeFinished: logEntry.DatetimeFinished,
|
||||||
|
Stdout: logEntry.Stdout,
|
||||||
|
Stderr: logEntry.Stderr,
|
||||||
|
TimedOut: logEntry.TimedOut,
|
||||||
|
Blocked: logEntry.Blocked,
|
||||||
|
ExitCode: logEntry.ExitCode,
|
||||||
|
Tags: logEntry.Tags,
|
||||||
|
ExecutionUuid: logEntry.UUID,
|
||||||
|
ExecutionStarted: logEntry.ExecutionStarted,
|
||||||
|
ExecutionFinished: logEntry.ExecutionFinished,
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
func (api *oliveTinAPI) WatchExecution(req *pb.WatchExecutionRequest, srv pb.OliveTinApi_WatchExecutionServer) error {
|
||||||
|
log.Infof("Watch")
|
||||||
|
|
||||||
|
if logEntry, ok := api.executor.Logs[req.ExecutionUuid]; !ok {
|
||||||
|
log.Errorf("Execution not found: %v", req.ExecutionUuid)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
if logEntry.ExecutionStarted {
|
||||||
|
for !logEntry.ExecutionCompleted {
|
||||||
|
tmp := make([]byte, 256)
|
||||||
|
|
||||||
|
red, err := io.ReadAtLeast(logEntry.StdoutBuffer, tmp, 1)
|
||||||
|
|
||||||
|
log.Infof("%v %v", red, err)
|
||||||
|
|
||||||
|
srv.Send(&pb.WatchExecutionUpdate{
|
||||||
|
Update: string(tmp),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func (api *oliveTinAPI) GetDashboardComponents(ctx ctx.Context, req *pb.GetDashboardComponentsRequest) (*pb.GetDashboardComponentsResponse, error) {
|
func (api *oliveTinAPI) GetDashboardComponents(ctx ctx.Context, req *pb.GetDashboardComponentsRequest) (*pb.GetDashboardComponentsResponse, error) {
|
||||||
user := acl.UserFromContext(ctx, cfg)
|
user := acl.UserFromContext(ctx, cfg)
|
||||||
|
|
||||||
|
|
@ -62,19 +120,30 @@ func (api *oliveTinAPI) GetLogs(ctx ctx.Context, req *pb.GetLogsRequest) (*pb.Ge
|
||||||
|
|
||||||
// TODO Limit to 10 entries or something to prevent browser lag.
|
// TODO Limit to 10 entries or something to prevent browser lag.
|
||||||
|
|
||||||
for _, logEntry := range api.executor.Logs {
|
for uuid, logEntry := range api.executor.Logs {
|
||||||
ret.Logs = append(ret.Logs, &pb.LogEntry{
|
ret.Logs = append(ret.Logs, &pb.LogEntry{
|
||||||
ActionTitle: logEntry.ActionTitle,
|
ActionTitle: logEntry.ActionTitle,
|
||||||
ActionIcon: logEntry.ActionIcon,
|
ActionIcon: logEntry.ActionIcon,
|
||||||
Datetime: logEntry.Datetime,
|
DatetimeStarted: logEntry.DatetimeStarted,
|
||||||
Stdout: logEntry.Stdout,
|
DatetimeFinished: logEntry.DatetimeFinished,
|
||||||
Stderr: logEntry.Stderr,
|
Stdout: logEntry.Stdout,
|
||||||
TimedOut: logEntry.TimedOut,
|
Stderr: logEntry.Stderr,
|
||||||
ExitCode: logEntry.ExitCode,
|
TimedOut: logEntry.TimedOut,
|
||||||
Tags: logEntry.Tags,
|
Blocked: logEntry.Blocked,
|
||||||
|
ExitCode: logEntry.ExitCode,
|
||||||
|
Tags: logEntry.Tags,
|
||||||
|
ExecutionUuid: uuid,
|
||||||
|
ExecutionStarted: logEntry.ExecutionStarted,
|
||||||
|
ExecutionFinished: logEntry.ExecutionFinished,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sorter := func(i, j int) bool {
|
||||||
|
return ret.Logs[i].DatetimeStarted < ret.Logs[j].DatetimeStarted
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(ret.Logs, sorter)
|
||||||
|
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,7 +199,7 @@ func Start(globalConfig *config.Config, ex *executor.Executor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
grpcServer := grpc.NewServer()
|
grpcServer := grpc.NewServer()
|
||||||
pb.RegisterOliveTinApiServer(grpcServer, newServer(ex))
|
pb.RegisterOliveTinApiServiceServer(grpcServer, newServer(ex))
|
||||||
|
|
||||||
err = grpcServer.Serve(lis)
|
err = grpcServer.Serve(lis)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,11 @@ func actionsCfgToPb(cfgActions []config.Action, user *acl.AuthenticatedUser) *pb
|
||||||
|
|
||||||
func actionCfgToPb(action config.Action, user *acl.AuthenticatedUser) *pb.Action {
|
func actionCfgToPb(action config.Action, user *acl.AuthenticatedUser) *pb.Action {
|
||||||
btn := pb.Action{
|
btn := pb.Action{
|
||||||
Id: fmt.Sprintf("%x", md5.Sum([]byte(action.Title))),
|
Id: fmt.Sprintf("%x", md5.Sum([]byte(action.Title))),
|
||||||
Title: action.Title,
|
Title: action.Title,
|
||||||
Icon: action.Icon,
|
Icon: action.Icon,
|
||||||
CanExec: acl.IsAllowedExec(cfg, user, &action),
|
CanExec: acl.IsAllowedExec(cfg, user, &action),
|
||||||
|
PopupOnStart: action.PopupOnStart,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, cfgArg := range action.Arguments {
|
for _, cfgArg := range action.Arguments {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ func init() {
|
||||||
|
|
||||||
lis = bufconn.Listen(bufSize)
|
lis = bufconn.Listen(bufSize)
|
||||||
s := grpc.NewServer()
|
s := grpc.NewServer()
|
||||||
pb.RegisterOliveTinApiServer(s, newServer(ex))
|
pb.RegisterOliveTinApiServiceServer(s, newServer(ex))
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := s.Serve(lis); err != nil {
|
if err := s.Serve(lis); err != nil {
|
||||||
|
|
@ -39,7 +39,7 @@ func bufDialer(context.Context, string) (net.Conn, error) {
|
||||||
return lis.Dial()
|
return lis.Dial()
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNewTestServerAndClient(t *testing.T, injectedConfig *config.Config) (*grpc.ClientConn, pb.OliveTinApiClient) {
|
func getNewTestServerAndClient(t *testing.T, injectedConfig *config.Config) (*grpc.ClientConn, pb.OliveTinApiServiceClient) {
|
||||||
cfg = injectedConfig
|
cfg = injectedConfig
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
@ -50,7 +50,7 @@ func getNewTestServerAndClient(t *testing.T, injectedConfig *config.Config) (*gr
|
||||||
t.Fatalf("Failed to dial bufnet: %v", err)
|
t.Fatalf("Failed to dial bufnet: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
client := pb.NewOliveTinApiClient(conn)
|
client := pb.NewOliveTinApiServiceClient(conn)
|
||||||
|
|
||||||
return conn, client
|
return conn, client
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,10 @@ func parseRequestMetadata(ctx context.Context, req *http.Request) metadata.MD {
|
||||||
return md
|
return md
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetGlobalRestConfig(config *config.Config) {
|
||||||
|
cfg = config
|
||||||
|
}
|
||||||
|
|
||||||
func startRestAPIServer(globalConfig *config.Config) error {
|
func startRestAPIServer(globalConfig *config.Config) error {
|
||||||
cfg = globalConfig
|
cfg = globalConfig
|
||||||
|
|
||||||
|
|
@ -76,7 +80,7 @@ func startRestAPIServer(globalConfig *config.Config) error {
|
||||||
runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.HTTPBodyMarshaler{
|
runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.HTTPBodyMarshaler{
|
||||||
Marshaler: &runtime.JSONPb{
|
Marshaler: &runtime.JSONPb{
|
||||||
MarshalOptions: protojson.MarshalOptions{
|
MarshalOptions: protojson.MarshalOptions{
|
||||||
UseProtoNames: true,
|
UseProtoNames: false, // eg: canExec for js instead of can_exec from protobuf
|
||||||
EmitUnpopulated: true,
|
EmitUnpopulated: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -84,7 +88,7 @@ func startRestAPIServer(globalConfig *config.Config) error {
|
||||||
)
|
)
|
||||||
opts := []grpc.DialOption{grpc.WithInsecure()}
|
opts := []grpc.DialOption{grpc.WithInsecure()}
|
||||||
|
|
||||||
err := gw.RegisterOliveTinApiHandlerFromEndpoint(ctx, mux, cfg.ListenAddressGrpcActions, opts)
|
err := gw.RegisterOliveTinApiServiceHandlerFromEndpoint(ctx, mux, cfg.ListenAddressGrpcActions, opts)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Could not register REST API Handler %v", err)
|
log.Errorf("Could not register REST API Handler %v", err)
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,58 @@
|
||||||
package httpservers
|
package httpservers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseJwtToken(cookieValue string) (*jwt.Token, error) {
|
var (
|
||||||
|
pubKeyBytes []byte = nil
|
||||||
|
pubKey *rsa.PublicKey
|
||||||
|
)
|
||||||
|
|
||||||
|
func readPublicKey() error {
|
||||||
|
if pubKeyBytes != nil {
|
||||||
|
return nil // Already read.
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKeyBytes, err := os.ReadFile(cfg.AuthJwtPubKeyPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("couldn't read public key from file %s", cfg.AuthJwtPubKeyPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since the token is RSA (which we validated at the start of this function), the return type of this function actually has to be rsa.PublicKey!
|
||||||
|
pubKey, err = jwt.ParseRSAPublicKeyFromPEM(pubKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing public key object (from %s)", cfg.AuthJwtPubKeyPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseJwtTokenWithKey(cookieValue string) (*jwt.Token, error) {
|
||||||
|
err := readPublicKey()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return jwt.Parse(cookieValue, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"expected token algorithm '%v' but got '%v'",
|
||||||
|
jwt.SigningMethodRS256.Name,
|
||||||
|
token.Header)
|
||||||
|
}
|
||||||
|
return pubKey, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseJwtTokenWithoutKey(cookieValue string) (*jwt.Token, error) {
|
||||||
return jwt.Parse(cookieValue, func(token *jwt.Token) (interface{}, error) {
|
return jwt.Parse(cookieValue, func(token *jwt.Token) (interface{}, error) {
|
||||||
// Don't forget to validate the alg is what you expect:
|
// Don't forget to validate the alg is what you expect:
|
||||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||||
|
|
@ -20,6 +64,14 @@ func parseJwtToken(cookieValue string) (*jwt.Token, error) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseJwtToken(cookieValue string) (*jwt.Token, error) {
|
||||||
|
if cfg.AuthJwtPubKeyPath != "" { // activate this path only if pub key is specified
|
||||||
|
return parseJwtTokenWithKey(cookieValue)
|
||||||
|
} else {
|
||||||
|
return parseJwtTokenWithoutKey(cookieValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getClaimsFromJwtToken(cookieValue string) (jwt.MapClaims, error) {
|
func getClaimsFromJwtToken(cookieValue string) (jwt.MapClaims, error) {
|
||||||
token, err := parseJwtToken(cookieValue)
|
token, err := parseJwtToken(cookieValue)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
package httpservers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
config2 "github.com/OliveTin/OliveTin/internal/config"
|
||||||
|
"github.com/OliveTin/OliveTin/internal/cors"
|
||||||
|
"github.com/golang-jwt/jwt/v4"
|
||||||
|
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createKeys() (*rsa.PrivateKey, string) {
|
||||||
|
tmpFile, _ := os.CreateTemp(os.TempDir(), "olivetin-jwt-")
|
||||||
|
defer os.Remove(tmpFile.Name())
|
||||||
|
|
||||||
|
fmt.Println("Created File: " + tmpFile.Name())
|
||||||
|
|
||||||
|
privateKey, _ := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
pubKey := &privateKey.PublicKey
|
||||||
|
// https://stackoverflow.com/questions/13555085/save-and-load-crypto-rsa-privatekey-to-and-from-the-disk
|
||||||
|
pkixPubKey, _ := x509.MarshalPKIXPublicKey(pubKey)
|
||||||
|
pubPem := pem.EncodeToMemory(
|
||||||
|
&pem.Block{
|
||||||
|
Type: "RSA PUBLIC KEY",
|
||||||
|
Bytes: pkixPubKey,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := os.WriteFile(tmpFile.Name(), pubPem, 0755); err != nil {
|
||||||
|
fmt.Printf("error when dumping pubKey: %s \n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return privateKey, tmpFile.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBase(t *testing.T, expire int64, expectCode int) {
|
||||||
|
privateKey, publicKeyPath := createKeys()
|
||||||
|
|
||||||
|
// default config + overrides
|
||||||
|
config := config2.DefaultConfig()
|
||||||
|
config.AuthJwtPubKeyPath = publicKeyPath
|
||||||
|
config.AuthJwtClaimUsername = "sub"
|
||||||
|
config.AuthJwtClaimUserGroup = "olivetinGroup"
|
||||||
|
config.AuthJwtCookieName = "authorization_token"
|
||||||
|
SetGlobalRestConfig(config) // ugly, setting global var, we should pass configs as params to modules... :/
|
||||||
|
|
||||||
|
token := jwt.New(jwt.SigningMethodRS256)
|
||||||
|
|
||||||
|
claims := token.Claims.(jwt.MapClaims)
|
||||||
|
claims["nbf"] = time.Now().Unix() - 1000
|
||||||
|
claims["exp"] = time.Now().Unix() + expire
|
||||||
|
claims["sub"] = "test"
|
||||||
|
claims["olivetinGroup"] = "test"
|
||||||
|
|
||||||
|
tokenStr, _ := token.SignedString(privateKey)
|
||||||
|
|
||||||
|
// init mux endpoint like in restapi.go (but using dummy response handler)
|
||||||
|
mux := runtime.NewServeMux(
|
||||||
|
runtime.WithMetadata(parseRequestMetadata), // i am guessing this is critical middleware for authorizing request cookie
|
||||||
|
runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.HTTPBodyMarshaler{
|
||||||
|
Marshaler: &runtime.JSONPb{
|
||||||
|
MarshalOptions: protojson.MarshalOptions{
|
||||||
|
UseProtoNames: true,
|
||||||
|
EmitUnpopulated: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
mux.HandlePath("GET", "/", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
|
||||||
|
username, usergroup := parseJwtCookie(r)
|
||||||
|
if username == "" {
|
||||||
|
w.WriteHeader(403)
|
||||||
|
}
|
||||||
|
w.Write([]byte(fmt.Sprintf("username=%v, usergroup=%v", username, usergroup)))
|
||||||
|
})
|
||||||
|
|
||||||
|
// make server and attach handler
|
||||||
|
srv := &http.Server{Handler: cors.AllowCors(mux)}
|
||||||
|
lis, _ := net.Listen("tcp", ":1337")
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := srv.Serve(lis); err != nil {
|
||||||
|
t.Errorf("couldn't start server: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// make http client and send request to myself
|
||||||
|
client := &http.Client{}
|
||||||
|
req, _ := http.NewRequest("GET", "http://localhost:1337/", nil)
|
||||||
|
cookie := &http.Cookie{
|
||||||
|
Name: "authorization_token",
|
||||||
|
Value: tokenStr,
|
||||||
|
MaxAge: 300,
|
||||||
|
}
|
||||||
|
req.AddCookie(cookie)
|
||||||
|
res, err := client.Do(req)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
assert.Equal(t, expectCode, -1)
|
||||||
|
} else {
|
||||||
|
defer res.Body.Close()
|
||||||
|
assert.Equal(t, expectCode, res.StatusCode)
|
||||||
|
body, _ := io.ReadAll(res.Body)
|
||||||
|
fmt.Println(string(body))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJWTSignatureVerificationSucceeds(t *testing.T) {
|
||||||
|
testBase(t, 1000, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJWTSignatureVerificationFails(t *testing.T) {
|
||||||
|
testBase(t, -500, 403)
|
||||||
|
}
|
||||||
|
|
@ -10,10 +10,12 @@ away, and several other issues.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
config "github.com/OliveTin/OliveTin/internal/config"
|
config "github.com/OliveTin/OliveTin/internal/config"
|
||||||
|
"github.com/OliveTin/OliveTin/internal/websocket"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StartSingleHTTPFrontend will create a reverse proxy that proxies the API
|
// StartSingleHTTPFrontend will create a reverse proxy that proxies the API
|
||||||
|
|
@ -37,8 +39,12 @@ func StartSingleHTTPFrontend(cfg *config.Config) {
|
||||||
})
|
})
|
||||||
|
|
||||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Debugf("ui req: %q", r.URL)
|
if strings.Contains(r.Header.Get("Connection"), "Upgrade") {
|
||||||
webuiProxy.ServeHTTP(w, r)
|
websocket.HandleWebsocket(w, r)
|
||||||
|
} else {
|
||||||
|
log.Debugf("ui req: %q", r.URL)
|
||||||
|
webuiProxy.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package httpservers
|
package httpservers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
config "github.com/OliveTin/OliveTin/internal/config"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -9,6 +10,8 @@ import (
|
||||||
func TestGetWebuiDir(t *testing.T) {
|
func TestGetWebuiDir(t *testing.T) {
|
||||||
os.Chdir("../../") // go test sets the cwd to "httpservers" by default
|
os.Chdir("../../") // go test sets the cwd to "httpservers" by default
|
||||||
|
|
||||||
|
cfg = config.DefaultConfig()
|
||||||
|
|
||||||
dir := findWebuiDir()
|
dir := findWebuiDir()
|
||||||
|
|
||||||
assert.Equal(t, "./webui", dir, "Finding the webui dir")
|
assert.Equal(t, "./webui", dir, "Finding the webui dir")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
package websocket
|
||||||
|
|
||||||
|
import (
|
||||||
|
pb "github.com/OliveTin/OliveTin/gen/grpc"
|
||||||
|
"github.com/OliveTin/OliveTin/internal/executor"
|
||||||
|
ws "github.com/gorilla/websocket"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
var upgrader = ws.Upgrader{}
|
||||||
|
|
||||||
|
type WebsocketClient struct {
|
||||||
|
conn *ws.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
var clients []*WebsocketClient
|
||||||
|
|
||||||
|
var marshalOptions = protojson.MarshalOptions{
|
||||||
|
UseProtoNames: false, // eg: canExec for js instead of can_exec from protobuf
|
||||||
|
EmitUnpopulated: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var ExecutionListener WebsocketExecutionListener
|
||||||
|
|
||||||
|
type WebsocketExecutionListener struct{}
|
||||||
|
|
||||||
|
func (WebsocketExecutionListener) OnExecutionStarted(title string) {
|
||||||
|
/*
|
||||||
|
broadcast(ExecutionStarted{
|
||||||
|
Type: "ExecutionStarted",
|
||||||
|
Action: title,
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
func (WebsocketExecutionListener) OnExecutionFinished(logEntry *executor.InternalLogEntry) {
|
||||||
|
le := &pb.LogEntry{
|
||||||
|
ActionTitle: logEntry.ActionTitle,
|
||||||
|
ActionIcon: logEntry.ActionIcon,
|
||||||
|
DatetimeStarted: logEntry.DatetimeStarted,
|
||||||
|
DatetimeFinished: logEntry.DatetimeFinished,
|
||||||
|
Stdout: logEntry.Stdout,
|
||||||
|
Stderr: logEntry.Stderr,
|
||||||
|
TimedOut: logEntry.TimedOut,
|
||||||
|
Blocked: logEntry.Blocked,
|
||||||
|
ExitCode: logEntry.ExitCode,
|
||||||
|
Tags: logEntry.Tags,
|
||||||
|
Uuid: logEntry.UUID,
|
||||||
|
ExecutionStarted: logEntry.ExecutionStarted,
|
||||||
|
ExecutionFinished: logEntry.ExecutionFinished,
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcast("ExecutionFinished", le)
|
||||||
|
}
|
||||||
|
|
||||||
|
func broadcast(messageType string, pbmsg *pb.LogEntry) {
|
||||||
|
payload, err := marshalOptions.Marshal(pbmsg)
|
||||||
|
|
||||||
|
// <EVIL>
|
||||||
|
// So, the websocket wants to encode messages using the same protomarshaller
|
||||||
|
// as the REST API - this gives consistency instead of using encoding/json
|
||||||
|
// and allows us to set specific marshalOptions.
|
||||||
|
//
|
||||||
|
// However, the protomarshaller will marshal the type, but the JavaScript at
|
||||||
|
// the other end has no idea what type this object is - as we're just sending
|
||||||
|
// it as JSON over the websocket.
|
||||||
|
//
|
||||||
|
// Therefore, we wrap the nicely marsheled bytes in a hacky JSON string
|
||||||
|
// literal and encode that string just with a byte array cast.
|
||||||
|
hackyMessageEnvelope := "{\"type\": \"" + messageType + "\", \"payload\": "
|
||||||
|
|
||||||
|
hackyMessage := []byte{}
|
||||||
|
hackyMessage = append(hackyMessage, []byte(hackyMessageEnvelope)...)
|
||||||
|
hackyMessage = append(hackyMessage, payload...)
|
||||||
|
hackyMessage = append(hackyMessage, []byte("}")...)
|
||||||
|
// </EVIL>
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("websocket marshal error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, client := range clients {
|
||||||
|
client.conn.WriteMessage(1, hackyMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *WebsocketClient) messageLoop() {
|
||||||
|
for {
|
||||||
|
mt, message, err := c.conn.ReadMessage()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("err: %v", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("websocket recv: %s %d", message, mt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleWebsocket(w http.ResponseWriter, r *http.Request) bool {
|
||||||
|
c, err := upgrader.Upgrade(w, r, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Websocket issue: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// defer c.Close()
|
||||||
|
|
||||||
|
wsclient := &WebsocketClient{
|
||||||
|
conn: c,
|
||||||
|
}
|
||||||
|
|
||||||
|
clients = append(clients, wsclient)
|
||||||
|
|
||||||
|
go wsclient.messageLoop()
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
@ -48,7 +48,7 @@ stop()
|
||||||
|
|
||||||
status() {
|
status() {
|
||||||
PID=$(pidof OliveTin)
|
PID=$(pidof OliveTin)
|
||||||
RETVAL=$?
|
RETVAL=$?
|
||||||
|
|
||||||
if [ $RETVAL -eq 1 ] ; then
|
if [ $RETVAL -eq 1 ] ; then
|
||||||
echo "OliveTin is stopped"
|
echo "OliveTin is stopped"
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,4 @@ name=$RC_SVCNAME
|
||||||
description="OliveTin"
|
description="OliveTin"
|
||||||
command="/usr/local/bin/OliveTin"
|
command="/usr/local/bin/OliveTin"
|
||||||
pidfile="/run/${RC_SVCNAME}.pid"
|
pidfile="/run/${RC_SVCNAME}.pid"
|
||||||
command_background=true
|
command_background=true
|
||||||
|
|
|
||||||
103
webui/index.html
103
webui/index.html
|
|
@ -62,47 +62,96 @@
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer title = "footer">
|
<footer title = "footer">
|
||||||
<p><img title = "application icon" src = "OliveTinLogo.png" height = "1em" class = "logo" /> OliveTin</p>
|
<p><img title = "application icon" src = "OliveTinLogo.png" alt = "OliveTin logo" height = "1em" class = "logo" /> OliveTin</p>
|
||||||
<p>
|
<p>
|
||||||
<a href = "https://docs.olivetin.app" target = "_new">Documentation</a> |
|
<a href = "https://docs.olivetin.app" target = "_new">Documentation</a> |
|
||||||
<a href = "https://github.com/OliveTin/OliveTin/issues/new/choose" target = "_new">Raise an issue on GitHub</a> |
|
<a href = "https://github.com/OliveTin/OliveTin/issues/new/choose" target = "_new">Raise an issue on GitHub</a> |
|
||||||
<span id = "currentVersion">Version: ?</p>
|
<span>Version: <span id = "currentVersion">?</span></span> |
|
||||||
|
<span>Server connection: <span id = "serverConnection">?</span></span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
<a id = "available-version" href = "http://olivetin.app" target = "_blank" hidden>?</a>
|
<a id = "available-version" href = "http://olivetin.app" target = "_blank" hidden>?</a>
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<dialog id = "executionResults">
|
||||||
|
<div class = "action-header">
|
||||||
|
<span class = "icon" role = "icon"></span>
|
||||||
|
|
||||||
|
<h2>Log:
|
||||||
|
<span class = "title">?</span>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
<strong>Started: </strong><span class = "datetimeStarted">unknown</span>.
|
||||||
|
<strong>Finished: </strong><span class = "datetimeFinished">unknown</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Exit Code: </strong><span class = "exitCode">unknown</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Status: </strong><span class = "status">unknown</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>stdout</summary>
|
||||||
|
<pre class = "stdout">
|
||||||
|
?
|
||||||
|
</pre>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>stderr</summary>
|
||||||
|
<pre class = "stderr">
|
||||||
|
?
|
||||||
|
</pre>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<form method = "dialog">
|
||||||
|
<button name = "cancel">Close</button>
|
||||||
|
</form>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
<template id = "tplArgumentForm">
|
<template id = "tplArgumentForm">
|
||||||
<form class = "action-arguments">
|
<dialog>
|
||||||
<div class = "wrapper">
|
<form class = "action-arguments">
|
||||||
<div>
|
<div class = "wrapper">
|
||||||
<span class = "icon" role = "icon"></span>
|
<div class = "action-header">
|
||||||
<h2>Argument form</h2>
|
<span class = "icon" role = "icon"></span>
|
||||||
</div>
|
<h2>Argument form</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class = "arguments"></div>
|
<div class = "arguments"></div>
|
||||||
|
|
||||||
<div class = "buttons">
|
<div class = "buttons">
|
||||||
<input name = "start" type = "submit" value = "Start">
|
<input name = "start" type = "submit" value = "Start">
|
||||||
<button name = "cancel">Cancel</button>
|
<button name = "cancel">Cancel</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
<form>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template id = "tplActionButton">
|
<template id = "tplActionButton">
|
||||||
<button>
|
<div>
|
||||||
<span role = "icon" title = "button icon" class = "icon">💩</span>
|
<button>
|
||||||
<p role = "title" class = "title">Untitled Button</p>
|
<span role = "icon" title = "button icon" class = "icon">💩</span>
|
||||||
</button>
|
<span role = "title" class = "title">Untitled Button</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class = "executions">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template id = "tplLogRow">
|
<template id = "tplLogRow">
|
||||||
<tr class = "log-row">
|
<tr class = "log-row">
|
||||||
<td class = "timestamp">?</td>
|
<td class = "timestamp">?</td>
|
||||||
<td>
|
<td>
|
||||||
<span class = "icon" role = "icon"></span>
|
<span class = "icon" role = "icon"></span>
|
||||||
<span class = "content">?</span>
|
<span class = "content">?</span>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>stdout</summary>
|
<summary>stdout</summary>
|
||||||
<pre class = "stdout">
|
<pre class = "stdout">
|
||||||
|
|
@ -124,12 +173,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script type = "text/javascript">
|
<script type = "text/javascript">
|
||||||
/**
|
/**
|
||||||
This is the bootstrap code, which relies on very simple, old javascript
|
This is the bootstrap code, which relies on very simple, old javascript
|
||||||
to at least display a helpful error message if we can't use OliveTin.
|
to at least display a helpful error message if we can't use OliveTin.
|
||||||
*/
|
*/
|
||||||
function showBigError (type, friendlyType, message) {
|
function showBigError (type, friendlyType, message) {
|
||||||
clearInterval(window.buttonInterval)
|
for (const oldError of document.querySelectorAll('div.error').values()) {
|
||||||
|
window.old = oldError;
|
||||||
|
oldError.remove();
|
||||||
|
}
|
||||||
|
|
||||||
console.error('Error ' + type + ': ', message)
|
console.error('Error ' + type + ': ', message)
|
||||||
|
|
||||||
|
|
@ -146,5 +198,6 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type = "module" src = "main.js"></script>
|
<script type = "module" src = "main.js"></script>
|
||||||
|
<script type = "module" src = "js/websocket.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { marshalLogsJsonToHtml } from './marshaller.js'
|
|
||||||
import './ArgumentForm.js'
|
import './ArgumentForm.js'
|
||||||
|
import './ExecutionButton.js'
|
||||||
|
|
||||||
class ActionButton extends window.HTMLElement {
|
class ActionButton extends window.HTMLElement {
|
||||||
constructFromJson (json) {
|
constructFromJson (json) {
|
||||||
|
|
@ -25,6 +25,7 @@ class ActionButton extends window.HTMLElement {
|
||||||
})
|
})
|
||||||
|
|
||||||
document.body.appendChild(frm)
|
document.body.appendChild(frm)
|
||||||
|
frm.querySelector('dialog').showModal()
|
||||||
} else {
|
} else {
|
||||||
this.startAction()
|
this.startAction()
|
||||||
}
|
}
|
||||||
|
|
@ -32,7 +33,8 @@ class ActionButton extends window.HTMLElement {
|
||||||
|
|
||||||
this.updateFromJson(json)
|
this.updateFromJson(json)
|
||||||
|
|
||||||
this.updateDom()
|
this.domTitle.innerText = this.btn.title
|
||||||
|
this.domIcon.innerHTML = this.unicodeIcon
|
||||||
|
|
||||||
this.setAttribute('id', 'actionButton_' + json.id)
|
this.setAttribute('id', 'actionButton_' + json.id)
|
||||||
}
|
}
|
||||||
|
|
@ -52,20 +54,27 @@ class ActionButton extends window.HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
startAction (actionArgs) {
|
startAction (actionArgs) {
|
||||||
this.btn.disabled = true
|
// this.btn.disabled = true
|
||||||
this.isWaiting = true
|
// this.isWaiting = true
|
||||||
this.updateDom()
|
// this.updateDom()
|
||||||
this.btn.classList = [] // Removes old animation classes
|
this.btn.classList = [] // Removes old animation classes
|
||||||
|
|
||||||
if (actionArgs === undefined) {
|
if (actionArgs === undefined) {
|
||||||
actionArgs = []
|
actionArgs = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UUIDs are create client side, so that we can setup a "execution-button"
|
||||||
|
// to track the execution before we send the request to the server.
|
||||||
const startActionArgs = {
|
const startActionArgs = {
|
||||||
actionName: this.btn.title,
|
actionName: this.btn.title,
|
||||||
arguments: actionArgs
|
arguments: actionArgs,
|
||||||
|
uuid: window.crypto.randomUUID()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const btnExecution = document.createElement('execution-button')
|
||||||
|
btnExecution.constructFromJson(startActionArgs.uuid)
|
||||||
|
this.querySelector('div.executions').appendChild(btnExecution)
|
||||||
|
|
||||||
window.fetch(this.actionCallUrl, {
|
window.fetch(this.actionCallUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
|
|
@ -80,45 +89,12 @@ class ActionButton extends window.HTMLElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
).then((json) => {
|
).then((json) => {
|
||||||
marshalLogsJsonToHtml({ logs: [json.logEntry] })
|
// The button used to wait for the action to finish, but now it is fire & forget
|
||||||
|
|
||||||
if (json.logEntry.timedOut) {
|
|
||||||
this.onActionResult('action-timeout', 'Timed out')
|
|
||||||
} else if (json.logEntry.exitCode === -1337) {
|
|
||||||
this.onActionError('Error')
|
|
||||||
} else if (json.logEntry.exitCode !== 0) {
|
|
||||||
this.onActionResult('action-nonzero-exit', 'Exit code ' + json.logEntry.exitCode)
|
|
||||||
} else {
|
|
||||||
this.onActionResult('action-success', 'Success!')
|
|
||||||
}
|
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
this.onActionError(err)
|
this.onActionError(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onActionResult (cssClass, temporaryStatusMessage) {
|
|
||||||
this.btn.disabled = false
|
|
||||||
this.temporaryStatusMessage = '[ ' + temporaryStatusMessage + ' ]'
|
|
||||||
this.updateDom()
|
|
||||||
this.btn.classList.add(cssClass)
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.btn.classList.remove(cssClass)
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
onActionError (err) {
|
|
||||||
console.error('callback error', err)
|
|
||||||
this.btn.disabled = false
|
|
||||||
this.isWaiting = false
|
|
||||||
this.updateDom()
|
|
||||||
this.btn.classList.add('action-failed')
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.btn.classList.remove('action-failed')
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
constructDomFromTemplate () {
|
constructDomFromTemplate () {
|
||||||
const tpl = document.getElementById('tplActionButton')
|
const tpl = document.getElementById('tplActionButton')
|
||||||
const content = tpl.content.cloneNode(true)
|
const content = tpl.content.cloneNode(true)
|
||||||
|
|
@ -134,27 +110,6 @@ class ActionButton extends window.HTMLElement {
|
||||||
this.domTitle = this.btn.querySelector('.title')
|
this.domTitle = this.btn.querySelector('.title')
|
||||||
this.domIcon = this.btn.querySelector('.icon')
|
this.domIcon = this.btn.querySelector('.icon')
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDom () {
|
|
||||||
if (this.temporaryStatusMessage != null) {
|
|
||||||
this.domTitle.innerText = this.temporaryStatusMessage
|
|
||||||
this.domTitle.classList.add('temporary-status-message')
|
|
||||||
this.isWaiting = false
|
|
||||||
this.disabled = false
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.temporaryStatusMessage = null
|
|
||||||
this.domTitle.classList.remove('temporary-status-message')
|
|
||||||
this.updateDom()
|
|
||||||
}, 2000)
|
|
||||||
} else if (this.isWaiting) {
|
|
||||||
this.domTitle.innerText = 'Waiting...'
|
|
||||||
} else {
|
|
||||||
this.domTitle.innerText = this.btn.title
|
|
||||||
}
|
|
||||||
|
|
||||||
this.domIcon.innerHTML = this.unicodeIcon
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('action-button', ActionButton)
|
window.customElements.define('action-button', ActionButton)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
import { ExecutionDialog } from './ExecutionDialog.js'
|
||||||
|
|
||||||
|
class ExecutionButton extends window.HTMLElement {
|
||||||
|
constructFromJson (json) {
|
||||||
|
this.executionUuid = json
|
||||||
|
|
||||||
|
this.appendChild(document.createElement('button'))
|
||||||
|
this.isWaiting = true
|
||||||
|
|
||||||
|
this.setAttribute('id', 'execution-' + json)
|
||||||
|
|
||||||
|
this.btn = this.querySelector('button')
|
||||||
|
this.btn.innerText = 'Executing...'
|
||||||
|
this.btn.onclick = () => {
|
||||||
|
this.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
show () {
|
||||||
|
if (window.executionDialog === undefined) {
|
||||||
|
window.executionDialog = new ExecutionDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
const executionStatusArgs = {
|
||||||
|
executionUuid: this.executionUuid
|
||||||
|
}
|
||||||
|
|
||||||
|
window.executionDialog.constructFromJson(this.executionUuid)
|
||||||
|
window.executionDialog.show()
|
||||||
|
|
||||||
|
window.fetch(window.restBaseUrl + 'ExecutionStatus', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(executionStatusArgs)
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.ok) {
|
||||||
|
return res.json()
|
||||||
|
} else {
|
||||||
|
throw new Error(res.statusText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).then((json) => {
|
||||||
|
window.executionDialog.renderResult(json)
|
||||||
|
}).catch(err => {
|
||||||
|
window.executionDialog.renderError(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onFinished (LogEntry) {
|
||||||
|
if (LogEntry.timedOut) {
|
||||||
|
this.onActionResult('action-timeout', 'Timed out')
|
||||||
|
} else if (LogEntry.blocked) {
|
||||||
|
this.onActionResult('action-blocked', 'Blocked!')
|
||||||
|
} else if (LogEntry.exitCode !== 0) {
|
||||||
|
this.onActionResult('action-nonzero-exit', 'Exit code ' + LogEntry.exitCode)
|
||||||
|
} else {
|
||||||
|
this.onActionResult('action-success', 'Success!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onActionResult (cssClass, temporaryStatusMessage) {
|
||||||
|
this.temporaryStatusMessage = '[ ' + temporaryStatusMessage + ' ]'
|
||||||
|
this.updateDom()
|
||||||
|
this.btn.classList.add(cssClass)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.btn.classList.remove(cssClass)
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
onActionError (err) {
|
||||||
|
console.error('callback error', err)
|
||||||
|
this.btn.disabled = false
|
||||||
|
this.isWaiting = false
|
||||||
|
this.updateDom()
|
||||||
|
this.btn.classList.add('action-failed')
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.btn.classList.remove('action-failed')
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDom () {
|
||||||
|
if (this.temporaryStatusMessage != null) {
|
||||||
|
this.btn.innerText = this.temporaryStatusMessage
|
||||||
|
this.btn.classList.add('temporary-status-message')
|
||||||
|
this.isWaiting = false
|
||||||
|
this.disabled = false
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.temporaryStatusMessage = null
|
||||||
|
this.btn.classList.remove('temporary-status-message')
|
||||||
|
this.updateDom()
|
||||||
|
}, 2000)
|
||||||
|
} else if (this.isWaiting) {
|
||||||
|
this.btn.innerText = 'Waiting...'
|
||||||
|
} else {
|
||||||
|
this.btn.innerText = 'Finished'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.customElements.define('execution-button', ExecutionButton)
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
// This ExecutionDialog is NOT a custom HTML element, but rather just picks up
|
||||||
|
// the <dialog /> element out of index.html and just re-uses that - as only
|
||||||
|
// one dialog can be shown at a time.
|
||||||
|
export class ExecutionDialog {
|
||||||
|
constructFromJson (json) {
|
||||||
|
this.executionUuid = json
|
||||||
|
|
||||||
|
this.dlg = document.querySelector('dialog#executionResults')
|
||||||
|
|
||||||
|
this.domIcon = this.dlg.querySelector('.icon')
|
||||||
|
this.domTitle = this.dlg.querySelector('.title')
|
||||||
|
this.domStdout = this.dlg.querySelector('.stdout')
|
||||||
|
this.domStderr = this.dlg.querySelector('.stderr')
|
||||||
|
this.domDatetimeStarted = this.dlg.querySelector('.datetimeStarted')
|
||||||
|
this.domDatetimeFinished = this.dlg.querySelector('.datetimeFinished')
|
||||||
|
this.domExitCode = this.dlg.querySelector('.exitCode')
|
||||||
|
this.domStatus = this.dlg.querySelector('.status')
|
||||||
|
}
|
||||||
|
|
||||||
|
show () {
|
||||||
|
this.dlg.showModal()
|
||||||
|
}
|
||||||
|
|
||||||
|
renderResult (res) {
|
||||||
|
this.executionUuid = res.logEntry.executionUuid
|
||||||
|
|
||||||
|
if (res.logEntry.executionFinished) {
|
||||||
|
this.domStatus.innerText = 'Completed'
|
||||||
|
this.domDatetimeFinished.innerText = res.logEntry.datetimeFinished
|
||||||
|
|
||||||
|
if (res.logEntry.blocked) {
|
||||||
|
this.domStatus.innerText = 'Blocked'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.logEntry.timedOut) {
|
||||||
|
this.domExitCode.innerText = 'Timed out'
|
||||||
|
this.domStatus.innerText = 'Timed out'
|
||||||
|
} else {
|
||||||
|
this.domExitCode.innerText = res.logEntry.exitCode
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.domDatetimeFinished.innerText = 'Still running...'
|
||||||
|
this.domExitCode.innerText = 'Still running...'
|
||||||
|
this.domStatus.innerText = 'Still running...'
|
||||||
|
}
|
||||||
|
|
||||||
|
this.domIcon.innerHTML = res.logEntry.actionIcon
|
||||||
|
this.domTitle.innerText = res.logEntry.actionTitle
|
||||||
|
|
||||||
|
this.domStdout.innerText = res.logEntry.stdout
|
||||||
|
|
||||||
|
if (res.logEntry.stderr === '') {
|
||||||
|
this.domStderr.parentElement.hidden = true
|
||||||
|
this.domStderr.innerText = res.logEntry.stderr
|
||||||
|
} else {
|
||||||
|
this.domStderr.parentElement.hidden = false
|
||||||
|
this.domStderr.innerText = res.logEntry.stderr
|
||||||
|
}
|
||||||
|
|
||||||
|
this.domDatetimeStarted.innerText = res.logEntry.datetimeStarted
|
||||||
|
}
|
||||||
|
|
||||||
|
renderError (err) {
|
||||||
|
this.dlg.querySelector('pre').innerText = JSON.stringify(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -50,7 +50,7 @@ export function marshalLogsJsonToHtml (json) {
|
||||||
logTableExitCode += ' (timed out)'
|
logTableExitCode += ' (timed out)'
|
||||||
}
|
}
|
||||||
|
|
||||||
row.querySelector('.timestamp').innerText = logEntry.datetime
|
row.querySelector('.timestamp').innerText = logEntry.datetimeStarted
|
||||||
row.querySelector('.content').innerText = logEntry.actionTitle
|
row.querySelector('.content').innerText = logEntry.actionTitle
|
||||||
row.querySelector('.icon').innerHTML = logEntry.actionIcon
|
row.querySelector('.icon').innerHTML = logEntry.actionIcon
|
||||||
row.querySelector('pre.stdout').innerText = logEntry.stdout
|
row.querySelector('pre.stdout').innerText = logEntry.stdout
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { marshalLogsJsonToHtml } from './marshaller.js'
|
||||||
|
|
||||||
|
window.ws = null
|
||||||
|
|
||||||
|
export function checkWebsocketConnection () {
|
||||||
|
if (window.ws === null || window.ws.readyState === 3) {
|
||||||
|
reconnectWebsocket()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reconnectWebsocket () {
|
||||||
|
window.websocketAvailable = false
|
||||||
|
|
||||||
|
let proto = 'ws:'
|
||||||
|
|
||||||
|
if (window.location.protocol === 'https:') {
|
||||||
|
proto = 'wss:'
|
||||||
|
}
|
||||||
|
|
||||||
|
const websocketConnectionUrl = proto + window.location.host + '/websocket'
|
||||||
|
const ws = window.ws = new WebSocket(websocketConnectionUrl)
|
||||||
|
|
||||||
|
ws.addEventListener('open', websocketOnOpen)
|
||||||
|
ws.addEventListener('message', websocketOnMessage)
|
||||||
|
ws.addEventListener('error', websocketOnError)
|
||||||
|
ws.addEventListener('close', websocketOnClose)
|
||||||
|
}
|
||||||
|
|
||||||
|
function websocketOnOpen (evt) {
|
||||||
|
window.websocketAvailable = true
|
||||||
|
|
||||||
|
window.ws.send('monitor')
|
||||||
|
|
||||||
|
window.refreshLoop()
|
||||||
|
}
|
||||||
|
|
||||||
|
function websocketOnMessage (msg) {
|
||||||
|
// FIXME check msg status is OK
|
||||||
|
const j = JSON.parse(msg.data)
|
||||||
|
|
||||||
|
switch (j.type) {
|
||||||
|
case 'ExecutionFinished':
|
||||||
|
updatePageAfterFinished(j.payload)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
window.showBigError('Unknown message type from server: ' + j.type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePageAfterFinished (logEntry) {
|
||||||
|
document.querySelector('execution-button#execution-' + logEntry.uuid).onFinished(logEntry)
|
||||||
|
|
||||||
|
marshalLogsJsonToHtml({
|
||||||
|
logs: [logEntry]
|
||||||
|
})
|
||||||
|
|
||||||
|
// If the current execution dialog is open, update that too
|
||||||
|
if (window.executionDialog != null && window.executionDialog.dlg.open && window.executionDialog.executionUuid === logEntry.uuid) {
|
||||||
|
window.executionDialog.renderResult({
|
||||||
|
logEntry: logEntry
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function websocketOnError (err) {
|
||||||
|
window.websocketAvailable = false
|
||||||
|
window.refreshLoop()
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
function websocketOnClose () {
|
||||||
|
window.websocketAvailable = false
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
import { marshalActionButtonsJsonToHtml, marshalLogsJsonToHtml } from './js/marshaller.js'
|
import { marshalActionButtonsJsonToHtml, marshalLogsJsonToHtml } from './js/marshaller.js'
|
||||||
|
import { checkWebsocketConnection } from './js/websocket.js'
|
||||||
|
|
||||||
function showSection (name) {
|
function showSection (name) {
|
||||||
for (const otherName of ['Actions', 'Logs']) {
|
for (const otherName of ['Actions', 'Logs']) {
|
||||||
|
|
@ -19,15 +20,42 @@ function setupSections () {
|
||||||
showSection('Actions')
|
showSection('Actions')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function refreshLoop () {
|
||||||
|
if (window.websocketAvailable) {
|
||||||
|
document.querySelector('#serverConnection').classList.remove('error')
|
||||||
|
document.querySelector('#serverConnection').innerText = 'websocket'
|
||||||
|
|
||||||
|
// Websocket updates are streamed live, not updated on a loop.
|
||||||
|
} else if (window.restAvailable) {
|
||||||
|
// Fallback to rest, but try to reconnect the websocket anyway.
|
||||||
|
|
||||||
|
fetchGetDashboardComponents()
|
||||||
|
checkWebsocketConnection()
|
||||||
|
|
||||||
|
document.querySelector('#serverConnection').classList.remove('error')
|
||||||
|
document.querySelector('#serverConnection').innerText = 'rest'
|
||||||
|
|
||||||
|
fetchGetLogs()
|
||||||
|
} else {
|
||||||
|
document.querySelector('#serverConnection').innerText = 'disconnected, trying to reconnect...'
|
||||||
|
document.querySelector('#serverConnection').classList.add('error')
|
||||||
|
|
||||||
|
// Still try to fetch the dashboard, if successfull window.restAvailable = true
|
||||||
|
fetchGetDashboardComponents()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function fetchGetDashboardComponents () {
|
function fetchGetDashboardComponents () {
|
||||||
window.fetch(window.restBaseUrl + 'GetDashboardComponents', {
|
window.fetch(window.restBaseUrl + 'GetDashboardComponents', {
|
||||||
cors: 'cors'
|
cors: 'cors'
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
return res.json()
|
return res.json()
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
|
window.restAvailable = true
|
||||||
marshalActionButtonsJsonToHtml(res)
|
marshalActionButtonsJsonToHtml(res)
|
||||||
}).catch(err => {
|
}).catch(() => { // err is 1st arg
|
||||||
window.showBigError('fetch-buttons', 'getting buttons', err, 'blat')
|
window.restAvailable = false
|
||||||
|
// window.showBigError('fetch-buttons', 'getting buttons', err, 'blat')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,7 +83,7 @@ function processWebuiSettingsJson (settings) {
|
||||||
document.head.appendChild(themeCss)
|
document.head.appendChild(themeCss)
|
||||||
}
|
}
|
||||||
|
|
||||||
document.querySelector('#currentVersion').innerText = 'Version: ' + settings.CurrentVersion
|
document.querySelector('#currentVersion').innerText = settings.CurrentVersion
|
||||||
|
|
||||||
if (settings.ShowNewVersions && settings.AvailableVersion !== 'none') {
|
if (settings.ShowNewVersions && settings.AvailableVersion !== 'none') {
|
||||||
document.querySelector('#available-version').innerText = 'New Version Available: ' + settings.AvailableVersion
|
document.querySelector('#available-version').innerText = 'New Version Available: ' + settings.AvailableVersion
|
||||||
|
|
@ -80,10 +108,11 @@ function main () {
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
processWebuiSettingsJson(res)
|
processWebuiSettingsJson(res)
|
||||||
|
|
||||||
fetchGetDashboardComponents()
|
window.restAvailable = true
|
||||||
fetchGetLogs()
|
window.refreshLoop = refreshLoop
|
||||||
|
window.refreshLoop()
|
||||||
|
|
||||||
window.buttonInterval = setInterval(fetchGetDashboardComponents, 3000)
|
setInterval(refreshLoop, 3000)
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
window.showBigError('fetch-webui-settings', 'getting webui settings', err)
|
window.showBigError('fetch-webui-settings', 'getting webui settings', err)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1053,9 +1053,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-node/node_modules/semver": {
|
"node_modules/eslint-plugin-node/node_modules/semver": {
|
||||||
"version": "6.3.0",
|
"version": "6.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
|
|
@ -2639,9 +2639,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/read-pkg/node_modules/semver": {
|
"node_modules/read-pkg/node_modules/semver": {
|
||||||
"version": "5.7.1",
|
"version": "5.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver"
|
"semver": "bin/semver"
|
||||||
|
|
@ -2796,9 +2796,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.3.8",
|
"version": "7.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||||
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
|
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lru-cache": "^6.0.0"
|
"lru-cache": "^6.0.0"
|
||||||
|
|
@ -4155,9 +4155,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "6.3.0",
|
"version": "6.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5281,9 +5281,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "5.7.1",
|
"version": "5.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"type-fest": {
|
"type-fest": {
|
||||||
|
|
@ -5399,9 +5399,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "7.3.8",
|
"version": "7.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||||
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
|
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"lru-cache": "^6.0.0"
|
"lru-cache": "^6.0.0"
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,13 @@ body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dialog {
|
||||||
|
min-width: 600px;
|
||||||
|
text-align: left;
|
||||||
|
padding: 1em;
|
||||||
|
box-shadow: 0 0 6px 0 #444;
|
||||||
|
}
|
||||||
|
|
||||||
fieldset {
|
fieldset {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
@ -120,7 +127,7 @@ span[role="icon"] {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
form span[role="icon"],
|
.action-header span[role="icon"],
|
||||||
tr.log-row span[role="icon"] {
|
tr.log-row span[role="icon"] {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding-right: 0.2em;
|
padding-right: 0.2em;
|
||||||
|
|
@ -128,6 +135,9 @@ tr.log-row span[role="icon"] {
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
background-color: salmon;
|
background-color: salmon;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.error {
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,6 +156,7 @@ div.entity {
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
|
margin-top: 0;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
@ -185,6 +196,11 @@ input[type="submit"]:disabled {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.executions button {
|
||||||
|
margin-top: .2em;
|
||||||
|
margin-bottom: .2em;
|
||||||
|
}
|
||||||
|
|
||||||
fieldset#section-switcher {
|
fieldset#section-switcher {
|
||||||
border: 0;
|
border: 0;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|
@ -254,6 +270,14 @@ button.active-section {
|
||||||
20% { background-color: cyan; }
|
20% { background-color: cyan; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-blocked {
|
||||||
|
animation: kf-action-blocked 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes kf-action-blocked {
|
||||||
|
20% { background-color: purple; }
|
||||||
|
}
|
||||||
|
|
||||||
footer,
|
footer,
|
||||||
footer a {
|
footer a {
|
||||||
color: black;
|
color: black;
|
||||||
|
|
@ -282,21 +306,9 @@ details[open] {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.action-arguments {
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
padding: 1em;
|
|
||||||
box-shadow: 0 0 6px 0 #aaa;
|
|
||||||
background-color: #dee3e7;
|
|
||||||
}
|
|
||||||
|
|
||||||
form div.wrapper {
|
form div.wrapper {
|
||||||
border-radius: 1em;
|
|
||||||
box-shadow: 0 0 10px 0 #444;
|
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border: 1px solid #999;
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 1em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue