Compare commits
1 Commits
v2023.4.1-
...
auth
Author | SHA1 | Date | |
---|---|---|---|
ee69644699 |
5
.gitignore
vendored
5
.gitignore
vendored
@ -4,9 +4,8 @@ dist/
|
|||||||
/tools
|
/tools
|
||||||
/tmp
|
/tmp
|
||||||
/state.json
|
/state.json
|
||||||
/emissary.sqlite*
|
/emissary.sqlite
|
||||||
/.gitea-release
|
/.gitea-release
|
||||||
/agent-key.json
|
/agent-key.json
|
||||||
/apps
|
/apps
|
||||||
/server-key.json
|
/server-key.json
|
||||||
/.emissary-token
|
|
@ -2,7 +2,6 @@ project_name: emissary
|
|||||||
before:
|
before:
|
||||||
hooks:
|
hooks:
|
||||||
- go mod tidy
|
- go mod tidy
|
||||||
- go generate ./...
|
|
||||||
builds:
|
builds:
|
||||||
- id: emissary-server
|
- id: emissary-server
|
||||||
env:
|
env:
|
||||||
@ -46,9 +45,6 @@ builds:
|
|||||||
- arm64
|
- arm64
|
||||||
- arm
|
- arm
|
||||||
- "386"
|
- "386"
|
||||||
goarm:
|
|
||||||
- "6"
|
|
||||||
- "7"
|
|
||||||
main: ./cmd/agent
|
main: ./cmd/agent
|
||||||
archives:
|
archives:
|
||||||
- id: server
|
- id: server
|
||||||
@ -67,7 +63,7 @@ archives:
|
|||||||
checksum:
|
checksum:
|
||||||
name_template: 'checksums.txt'
|
name_template: 'checksums.txt'
|
||||||
snapshot:
|
snapshot:
|
||||||
name_template: "{{ .Version }}"
|
name_template: "{{ incpatch .Version }}-next"
|
||||||
changelog:
|
changelog:
|
||||||
sort: asc
|
sort: asc
|
||||||
filters:
|
filters:
|
||||||
|
70
Jenkinsfile
vendored
70
Jenkinsfile
vendored
@ -1,70 +0,0 @@
|
|||||||
@Library('cadoles') _
|
|
||||||
|
|
||||||
pipeline {
|
|
||||||
agent {
|
|
||||||
dockerfile {
|
|
||||||
label 'docker'
|
|
||||||
filename 'Dockerfile'
|
|
||||||
dir 'misc/jenkins'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stages {
|
|
||||||
stage('Run unit tests') {
|
|
||||||
steps {
|
|
||||||
script {
|
|
||||||
withCredentials([
|
|
||||||
usernamePassword([
|
|
||||||
credentialsId: 'forge-jenkins',
|
|
||||||
usernameVariable: 'GIT_USERNAME',
|
|
||||||
passwordVariable: 'GIT_PASSWORD'
|
|
||||||
])
|
|
||||||
]) {
|
|
||||||
sh '''
|
|
||||||
git config --global credential.https://forge.cadoles.com.username "$GIT_USERNAME"
|
|
||||||
git config --global credential.https://forge.cadoles.com.helper '!f() { test "$1" = get && echo "password=$GIT_PASSWORD"; }; f'
|
|
||||||
|
|
||||||
export GOPRIVATE=forge.cadoles.com/arcad/edge
|
|
||||||
make test
|
|
||||||
'''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage('Release') {
|
|
||||||
when {
|
|
||||||
anyOf {
|
|
||||||
branch 'master'
|
|
||||||
branch 'develop'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
steps {
|
|
||||||
script {
|
|
||||||
withCredentials([
|
|
||||||
usernamePassword([
|
|
||||||
credentialsId: 'forge-jenkins',
|
|
||||||
usernameVariable: 'GITEA_RELEASE_USERNAME',
|
|
||||||
passwordVariable: 'GITEA_RELEASE_PASSWORD'
|
|
||||||
])
|
|
||||||
]) {
|
|
||||||
sh 'make gitea-release'
|
|
||||||
}
|
|
||||||
def currentVersion = sh(returnStdout: true, script: 'make full-version').trim()
|
|
||||||
build(
|
|
||||||
job: "../emissary-firmware/${env.GIT_BRANCH}",
|
|
||||||
parameters: [
|
|
||||||
[$class: 'StringParameterValue', name: 'emissaryRelease', value: currentVersion]
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
post {
|
|
||||||
always {
|
|
||||||
cleanWs()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
34
Makefile
34
Makefile
@ -1,16 +1,14 @@
|
|||||||
LINT_ARGS ?= --timeout 5m
|
LINT_ARGS ?= --timeout 5m
|
||||||
GORELEASER_VERSION ?= v1.13.1
|
GORELEASER_VERSION ?= v1.13.1
|
||||||
GORELEASER_ARGS ?= release --snapshot --rm-dist
|
GORELEASER_ARGS ?= release --auto-snapshot --snapshot --rm-dist
|
||||||
GITCHLOG_ARGS ?=
|
GITCHLOG_ARGS ?=
|
||||||
SHELL := /bin/bash
|
SHELL := /bin/bash
|
||||||
|
|
||||||
EMISSARY_VERSION ?=
|
EMISSARY_VERSION ?=
|
||||||
GIT_VERSION := $(shell git describe --always)
|
GIT_VERSION ?= $(shell git describe --always)
|
||||||
DATE_VERSION := $(shell date +%Y.%-m.%-d)
|
|
||||||
FULL_VERSION := v$(DATE_VERSION)-$(GIT_VERSION)$(if $(shell git diff --stat),-dirty,)
|
|
||||||
|
|
||||||
DOCKER_IMAGE_NAME ?= bornholm/emissary
|
DOCKER_IMAGE_NAME ?= bornholm/emissary
|
||||||
DOCKER_IMAGE_TAG ?= $(FULL_VERSION)
|
DOCKER_IMAGE_TAG ?= $(GIT_VERSION)$(if $(shell git diff --stat),-dirty,)
|
||||||
|
|
||||||
GOTEST_ARGS ?= -short
|
GOTEST_ARGS ?= -short
|
||||||
|
|
||||||
@ -45,7 +43,7 @@ build-emissary-%: deps ## Build executable
|
|||||||
-v \
|
-v \
|
||||||
-ldflags "\
|
-ldflags "\
|
||||||
-X 'main.GitRef=$(shell git rev-parse --short HEAD)' \
|
-X 'main.GitRef=$(shell git rev-parse --short HEAD)' \
|
||||||
-X 'main.ProjectVersion=$(FULL_VERSION)' \
|
-X 'main.ProjectVersion=$(shell git describe --always)' \
|
||||||
-X 'main.BuildDate=$(shell date --utc --rfc-3339=seconds)' \
|
-X 'main.BuildDate=$(shell date --utc --rfc-3339=seconds)' \
|
||||||
" \
|
" \
|
||||||
-o ./bin/$* \
|
-o ./bin/$* \
|
||||||
@ -75,7 +73,7 @@ dump-config: build-emissary
|
|||||||
|
|
||||||
.PHONY: goreleaser
|
.PHONY: goreleaser
|
||||||
goreleaser: deps
|
goreleaser: deps
|
||||||
( set -o allexport && source .env && set +o allexport && VERSION=$(GORELEASER_VERSION) curl -sfL https://goreleaser.com/static/run | GORELEASER_CURRENT_TAG="$(FULL_VERSION)" bash /dev/stdin $(GORELEASER_ARGS) )
|
( set -o allexport && source .env && set +o allexport && VERSION=$(GORELEASER_VERSION) curl -sfL https://goreleaser.com/static/run | bash /dev/stdin $(GORELEASER_ARGS) )
|
||||||
|
|
||||||
.PHONY: start-release
|
.PHONY: start-release
|
||||||
start-release:
|
start-release:
|
||||||
@ -124,33 +122,19 @@ gitea-release: tools/gitea-release/bin/gitea-release.sh goreleaser
|
|||||||
rm -rf .gitea-release/*
|
rm -rf .gitea-release/*
|
||||||
|
|
||||||
cp dist/*.tar.gz .gitea-release/
|
cp dist/*.tar.gz .gitea-release/
|
||||||
cp dist/*.apk .gitea-release/
|
|
||||||
cp dist/*.deb .gitea-release/
|
|
||||||
|
|
||||||
GITEA_RELEASE_PROJECT="emissary" \
|
GITEA_RELEASE_PROJECT="emissary" \
|
||||||
GITEA_RELEASE_ORG="arcad" \
|
GITEA_RELEASE_ORG="arcad" \
|
||||||
GITEA_RELEASE_BASE_URL="https://forge.cadoles.com" \
|
GITEA_RELEASE_BASE_URL="https://forge.cadoles.com" \
|
||||||
GITEA_RELEASE_VERSION="$(FULL_VERSION)" \
|
GITEA_RELEASE_VERSION="$(GIT_VERSION)" \
|
||||||
GITEA_RELEASE_NAME="$(FULL_VERSION)" \
|
GITEA_RELEASE_NAME="$(GIT_VERSION)" \
|
||||||
GITEA_RELEASE_COMMITISH_TARGET="$(GIT_VERSION)" \
|
GITEA_RELEASE_COMMITISH_TARGET="$(GIT_VERSION)" \
|
||||||
GITEA_RELEASE_IS_DRAFT="false" \
|
GITEA_RELEASE_IS_DRAFT="false" \
|
||||||
GITEA_RELEASE_BODY="" \
|
GITEA_RELEASE_BODY="" \
|
||||||
GITEA_RELEASE_ATTACHMENTS="$$(find .gitea-release/* -type f)" \
|
GITEA_RELEASE_ATTACHMENTS="$(shell find .gitea-release/* -type f)" \
|
||||||
tools/gitea-release/bin/gitea-release.sh
|
tools/gitea-release/bin/gitea-release.sh
|
||||||
|
|
||||||
tools/gitea-release/bin/gitea-release.sh:
|
tools/gitea-release/bin/gitea-release.sh:
|
||||||
mkdir -p tools/gitea-release/bin
|
mkdir -p tools/gitea-release/bin
|
||||||
curl --output tools/gitea-release/bin/gitea-release.sh https://forge.cadoles.com/Cadoles/Jenkins/raw/branch/master/resources/com/cadoles/gitea/gitea-release.sh
|
curl --output tools/gitea-release/bin/gitea-release.sh https://forge.cadoles.com/Cadoles/Jenkins/raw/branch/master/resources/com/cadoles/gitea/gitea-release.sh
|
||||||
chmod +x tools/gitea-release/bin/gitea-release.sh
|
chmod +x tools/gitea-release/bin/gitea-release.sh
|
||||||
|
|
||||||
.emissary-token:
|
|
||||||
$(MAKE) run-emissary-server EMISSARY_CMD="--debug --config tmp/server.yml server auth create-token --role writer > .emissary-token"
|
|
||||||
|
|
||||||
AGENT_ID ?= 1
|
|
||||||
|
|
||||||
load-sample-specs:
|
|
||||||
cat misc/spec-samples/app.emissary.cadoles.com.json | ./bin/server api agent spec update -a $(AGENT_ID) --no-patch --spec-data - --spec-name app.emissary.cadoles.com
|
|
||||||
cat misc/spec-samples/proxy.emissary.cadoles.com.json | ./bin/server api agent spec update -a $(AGENT_ID) --no-patch --spec-data - --spec-name proxy.emissary.cadoles.com
|
|
||||||
|
|
||||||
full-version:
|
|
||||||
@echo $(FULL_VERSION)
|
|
@ -5,10 +5,7 @@ import (
|
|||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command"
|
"forge.cadoles.com/Cadoles/emissary/internal/command"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/agent"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/agent"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/client"
|
||||||
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/imports/format"
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/imports/spec"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// nolint: gochecknoglobals
|
// nolint: gochecknoglobals
|
||||||
@ -20,5 +17,5 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
command.Main(BuildDate, ProjectVersion, GitRef, DefaultConfigPath, agent.Root(), api.Root())
|
command.Main(BuildDate, ProjectVersion, GitRef, DefaultConfigPath, agent.Root(), client.Root())
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command"
|
"forge.cadoles.com/Cadoles/emissary/internal/command"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/client"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/server"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/server"
|
||||||
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/imports/format"
|
_ "github.com/jackc/pgx/v5/stdlib"
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/imports/spec"
|
_ "modernc.org/sqlite"
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/imports/sql"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// nolint: gochecknoglobals
|
// nolint: gochecknoglobals
|
||||||
@ -21,5 +20,5 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
command.Main(BuildDate, ProjectVersion, GitRef, DefaultConfigPath, server.Root(), api.Root())
|
command.Main(BuildDate, ProjectVersion, GitRef, DefaultConfigPath, server.Root(), client.Root())
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,6 @@
|
|||||||
|
|
||||||
[See `misc/rest/server.rest`](../misc/rest/server.rest)
|
[See `misc/rest/server.rest`](../misc/rest/server.rest)
|
||||||
|
|
||||||
### Spécifications
|
|
||||||
|
|
||||||
- [Schéma `app.emissary.cadoles.com`](../internal/spec/app/schema.json)
|
|
||||||
- [Schéma `uci.emissary.cadoles.com`](../internal/spec/uci/schema.json)
|
|
||||||
- [Schéma `gateway.emissary.cadoles.com`](../internal/spec/gateway/schema.json)
|
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
See:
|
See:
|
||||||
|
41
go.mod
41
go.mod
@ -3,26 +3,23 @@ module forge.cadoles.com/Cadoles/emissary
|
|||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
forge.cadoles.com/arcad/edge v0.0.0-20230328183829-d8ce2901d2ab
|
forge.cadoles.com/arcad/edge v0.0.0-20230303153719-6399196fe5c9
|
||||||
github.com/Masterminds/sprig/v3 v3.2.3
|
|
||||||
github.com/alecthomas/participle/v2 v2.0.0-beta.5
|
github.com/alecthomas/participle/v2 v2.0.0-beta.5
|
||||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883
|
||||||
github.com/btcsuite/btcd/btcutil v1.1.3
|
github.com/btcsuite/btcd/btcutil v1.1.3
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/denisbrodbeck/machineid v1.0.1
|
github.com/denisbrodbeck/machineid v1.0.1
|
||||||
github.com/dop251/goja v0.0.0-20230304130813-e2f543bf4b4c
|
|
||||||
github.com/evanphx/json-patch/v5 v5.6.0
|
github.com/evanphx/json-patch/v5 v5.6.0
|
||||||
github.com/go-chi/chi v4.1.2+incompatible
|
github.com/go-chi/chi v4.1.2+incompatible
|
||||||
github.com/go-chi/cors v1.2.1
|
github.com/go-chi/cors v1.2.1
|
||||||
github.com/golang-migrate/migrate/v4 v4.15.2
|
github.com/golang-migrate/migrate/v4 v4.15.2
|
||||||
github.com/jackc/pgx/v5 v5.3.1
|
github.com/jackc/pgx/v5 v5.3.1
|
||||||
github.com/jedib0t/go-pretty/v6 v6.4.4
|
github.com/jedib0t/go-pretty/v6 v6.4.4
|
||||||
github.com/lestrrat-go/jwx/v2 v2.0.9
|
github.com/lestrrat-go/jwx/v2 v2.0.8
|
||||||
github.com/lithammer/shortuuid/v4 v4.0.0
|
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/qri-io/jsonschema v0.2.1
|
github.com/qri-io/jsonschema v0.2.1
|
||||||
|
github.com/santhosh-tekuri/jsonschema/v5 v5.1.1
|
||||||
github.com/urfave/cli/v2 v2.24.4
|
github.com/urfave/cli/v2 v2.24.4
|
||||||
gitlab.com/wpetit/goweb v0.0.0-20230227162855-a1f09bafccb3
|
gitlab.com/wpetit/goweb v0.0.0-20230227162855-a1f09bafccb3
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
@ -30,43 +27,35 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
|
||||||
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
|
||||||
github.com/barnybug/go-cast v0.0.0-20201201064555-a87ccbc26692 // indirect
|
github.com/barnybug/go-cast v0.0.0-20201201064555-a87ccbc26692 // indirect
|
||||||
github.com/dop251/goja_nodejs v0.0.0-20230320130059-dcf93ba651dd // indirect
|
github.com/dop251/goja v0.0.0-20230226152633-7c93113e17ac // indirect
|
||||||
|
github.com/dop251/goja_nodejs v0.0.0-20230226152057-060fa99b809f // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.1 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.1 // indirect
|
||||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/google/pprof v0.0.0-20230309165930-d61513b1440d // indirect
|
|
||||||
github.com/gorilla/websocket v1.5.0 // indirect
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
github.com/hashicorp/mdns v1.0.5 // indirect
|
github.com/hashicorp/mdns v1.0.5 // indirect
|
||||||
github.com/huandu/xstrings v1.3.3 // indirect
|
|
||||||
github.com/igm/sockjs-go/v3 v3.0.2 // indirect
|
github.com/igm/sockjs-go/v3 v3.0.2 // indirect
|
||||||
github.com/imdario/mergo v0.3.12 // indirect
|
|
||||||
github.com/miekg/dns v1.1.51 // indirect
|
github.com/miekg/dns v1.1.51 // indirect
|
||||||
github.com/mitchellh/copystructure v1.0.0 // indirect
|
|
||||||
github.com/mitchellh/reflectwalk v1.0.0 // indirect
|
|
||||||
github.com/oklog/ulid/v2 v2.1.0 // indirect
|
github.com/oklog/ulid/v2 v2.1.0 // indirect
|
||||||
github.com/orcaman/concurrent-map v1.0.0 // indirect
|
github.com/orcaman/concurrent-map v1.0.0 // indirect
|
||||||
github.com/shopspring/decimal v1.2.0 // indirect
|
golang.org/x/net v0.7.0 // indirect
|
||||||
github.com/spf13/cast v1.3.1 // indirect
|
|
||||||
golang.org/x/net v0.8.0 // indirect
|
|
||||||
google.golang.org/genproto v0.0.0-20220728213248-dd149ef739b9 // indirect
|
google.golang.org/genproto v0.0.0-20220728213248-dd149ef739b9 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cdr.dev/slog v1.4.2 // indirect
|
cdr.dev/slog v1.4.1 // indirect
|
||||||
github.com/alecthomas/chroma v0.10.0 // indirect
|
github.com/alecthomas/chroma v0.10.0 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
|
||||||
github.com/dlclark/regexp2 v1.8.1 // indirect
|
github.com/dlclark/regexp2 v1.8.1 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/fatih/color v1.15.0 // indirect
|
github.com/fatih/color v1.14.1 // indirect
|
||||||
github.com/go-chi/chi/v5 v5.0.8
|
github.com/go-chi/chi/v5 v5.0.8
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.9.11 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
@ -80,7 +69,7 @@ require (
|
|||||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||||
github.com/lestrrat-go/httprc v1.0.4 // indirect
|
github.com/lestrrat-go/httprc v1.0.4 // indirect
|
||||||
github.com/lestrrat-go/iter v1.0.2 // indirect
|
github.com/lestrrat-go/iter v1.0.2 // indirect
|
||||||
github.com/lestrrat-go/option v1.0.1 // indirect
|
github.com/lestrrat-go/option v1.0.0 // indirect
|
||||||
github.com/lib/pq v1.10.7 // indirect
|
github.com/lib/pq v1.10.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||||
@ -94,12 +83,12 @@ require (
|
|||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
go.uber.org/atomic v1.10.0 // indirect
|
go.uber.org/atomic v1.10.0 // indirect
|
||||||
golang.org/x/crypto v0.7.0 // indirect
|
golang.org/x/crypto v0.6.0 // indirect
|
||||||
golang.org/x/mod v0.8.0 // indirect
|
golang.org/x/mod v0.8.0 // indirect
|
||||||
golang.org/x/sync v0.1.0 // indirect
|
golang.org/x/sync v0.1.0 // indirect
|
||||||
golang.org/x/sys v0.6.0 // indirect
|
golang.org/x/sys v0.5.0 // indirect
|
||||||
golang.org/x/term v0.6.0 // indirect
|
golang.org/x/term v0.5.0 // indirect
|
||||||
golang.org/x/text v0.8.0 // indirect
|
golang.org/x/text v0.7.0 // indirect
|
||||||
golang.org/x/tools v0.6.0 // indirect
|
golang.org/x/tools v0.6.0 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||||
google.golang.org/protobuf v1.28.1 // indirect
|
google.golang.org/protobuf v1.28.1 // indirect
|
||||||
@ -114,5 +103,3 @@ require (
|
|||||||
modernc.org/strutil v1.1.3 // indirect
|
modernc.org/strutil v1.1.3 // indirect
|
||||||
modernc.org/token v1.1.0 // indirect
|
modernc.org/token v1.1.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
// replace forge.cadoles.com/arcad/edge => ../edge
|
|
||||||
|
93
go.sum
93
go.sum
@ -1,8 +1,8 @@
|
|||||||
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
|
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
|
||||||
bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM=
|
bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM=
|
||||||
cdr.dev/slog v1.4.0/go.mod h1:C5OL99WyuOK8YHZdYY57dAPN1jK2WJlCdq2VP6xeQns=
|
cdr.dev/slog v1.4.0/go.mod h1:C5OL99WyuOK8YHZdYY57dAPN1jK2WJlCdq2VP6xeQns=
|
||||||
cdr.dev/slog v1.4.2 h1:fIfiqASYQFJBZiASwL825atyzeA96NsqSxx2aL61P8I=
|
cdr.dev/slog v1.4.1 h1:Q8+X63m8/WB4geelMTDO8t4CTwVh1f7+5Cxi7kS/SZg=
|
||||||
cdr.dev/slog v1.4.2/go.mod h1:0EkH+GkFNxizNR+GAXUEdUHanxUH5t9zqPILmPM/Vn8=
|
cdr.dev/slog v1.4.1/go.mod h1:O76C6gZJxa5HK1SXMrjd48V2kJxYZKFRTcFfn/V9OhA=
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
@ -54,8 +54,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
|
|||||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
forge.cadoles.com/arcad/edge v0.0.0-20230328183829-d8ce2901d2ab h1:xOtzLAYOUcKd/VBx/PzL2riC0zNuQ/cxxf5r3AmEvJE=
|
forge.cadoles.com/arcad/edge v0.0.0-20230303153719-6399196fe5c9 h1:dleaaf/rV2GWtGvrPunRakjrKVDfXoANxAy8/1ctMVs=
|
||||||
forge.cadoles.com/arcad/edge v0.0.0-20230328183829-d8ce2901d2ab/go.mod h1:ONd6vyQ0IM0vHi1i+bmZBRc1Fd0BoXMuDdY/+0sZefw=
|
forge.cadoles.com/arcad/edge v0.0.0-20230303153719-6399196fe5c9/go.mod h1:pl9EMtSLSVr4wbDgQBDjr4aizwtmwasE686dm5arYPw=
|
||||||
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
|
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
|
||||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg=
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg=
|
||||||
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
|
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
|
||||||
@ -84,12 +84,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
|
|||||||
github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
|
github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
|
||||||
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
|
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
|
||||||
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
|
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
|
||||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
|
||||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
|
||||||
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
|
|
||||||
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
|
||||||
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
|
||||||
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
|
||||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||||
@ -129,11 +123,13 @@ github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm
|
|||||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
|
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
|
||||||
github.com/alecthomas/assert/v2 v2.0.3 h1:WKqJODfOiQG0nEJKFKzDIG3E29CN2/4zR9XGJzKIkbg=
|
github.com/alecthomas/assert/v2 v2.0.3 h1:WKqJODfOiQG0nEJKFKzDIG3E29CN2/4zR9XGJzKIkbg=
|
||||||
github.com/alecthomas/chroma v0.7.0/go.mod h1:1U/PfCsTALWWYHDnsIQkxEBM0+6LLe0v8+RSVMOwxeY=
|
github.com/alecthomas/chroma v0.7.0/go.mod h1:1U/PfCsTALWWYHDnsIQkxEBM0+6LLe0v8+RSVMOwxeY=
|
||||||
|
github.com/alecthomas/chroma v0.9.1/go.mod h1:eMuEnpA18XbG/WhOWtCzJHS7WqEtDAI+HxdwoW0nVSk=
|
||||||
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
|
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
|
||||||
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
|
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
|
||||||
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
|
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
|
||||||
github.com/alecthomas/kong v0.1.17-0.20190424132513-439c674f7ae0/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=
|
github.com/alecthomas/kong v0.1.17-0.20190424132513-439c674f7ae0/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=
|
||||||
github.com/alecthomas/kong v0.2.1-0.20190708041108-0548c6b1afae/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=
|
github.com/alecthomas/kong v0.2.1-0.20190708041108-0548c6b1afae/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=
|
||||||
|
github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
|
||||||
github.com/alecthomas/kong-hcl v0.1.8-0.20190615233001-b21fea9723c8/go.mod h1:MRgZdU3vrFd05IQ89AxUZ0aYdF39BYoNFa324SodPCA=
|
github.com/alecthomas/kong-hcl v0.1.8-0.20190615233001-b21fea9723c8/go.mod h1:MRgZdU3vrFd05IQ89AxUZ0aYdF39BYoNFa324SodPCA=
|
||||||
github.com/alecthomas/participle/v2 v2.0.0-beta.5 h1:y6dsSYVb1G5eK6mgmy+BgI3Mw35a3WghArZ/Hbebrjo=
|
github.com/alecthomas/participle/v2 v2.0.0-beta.5 h1:y6dsSYVb1G5eK6mgmy+BgI3Mw35a3WghArZ/Hbebrjo=
|
||||||
github.com/alecthomas/participle/v2 v2.0.0-beta.5/go.mod h1:RC764t6n4L8D8ITAJv0qdokritYSNR3wV5cVwmIEaMM=
|
github.com/alecthomas/participle/v2 v2.0.0-beta.5/go.mod h1:RC764t6n4L8D8ITAJv0qdokritYSNR3wV5cVwmIEaMM=
|
||||||
@ -240,11 +236,8 @@ github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOo
|
|||||||
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
|
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
|
||||||
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
|
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
|
||||||
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/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
|
|
||||||
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=
|
||||||
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
|
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
|
||||||
github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
|
github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
|
||||||
github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc=
|
github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc=
|
||||||
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
|
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
|
||||||
@ -450,12 +443,12 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ
|
|||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||||
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
|
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
|
||||||
github.com/dop251/goja v0.0.0-20221118162653-d4bf6fde1b86/go.mod h1:yRkwfj0CBpOGre+TwBsqPV0IH0Pk73e4PXJOeNDboGs=
|
github.com/dop251/goja v0.0.0-20221118162653-d4bf6fde1b86/go.mod h1:yRkwfj0CBpOGre+TwBsqPV0IH0Pk73e4PXJOeNDboGs=
|
||||||
github.com/dop251/goja v0.0.0-20230304130813-e2f543bf4b4c h1:/utv6nmTctV6OVgfk5+O6lEMEWL+6KJy4h9NZ5fnkQQ=
|
github.com/dop251/goja v0.0.0-20230226152633-7c93113e17ac h1:NGu46Adk2oPN3tinGFItahy4W9l+9uhEf03ZxbwmdVE=
|
||||||
github.com/dop251/goja v0.0.0-20230304130813-e2f543bf4b4c/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
|
github.com/dop251/goja v0.0.0-20230226152633-7c93113e17ac/go.mod h1:yRkwfj0CBpOGre+TwBsqPV0IH0Pk73e4PXJOeNDboGs=
|
||||||
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
|
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
|
||||||
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
|
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
|
||||||
github.com/dop251/goja_nodejs v0.0.0-20230320130059-dcf93ba651dd h1:8FguYHL/davT0sAfVoi84iRI4MCVTVFtlnmZqIoAXDQ=
|
github.com/dop251/goja_nodejs v0.0.0-20230226152057-060fa99b809f h1:mmnNidRg3cMfcgyeNtIBSDZgjf/85lA/2pplccwSxYg=
|
||||||
github.com/dop251/goja_nodejs v0.0.0-20230320130059-dcf93ba651dd/go.mod h1:0tlktQL7yHfYEtjcRGi/eiOkbDR5XF7gyFFvbC5//E0=
|
github.com/dop251/goja_nodejs v0.0.0-20230226152057-060fa99b809f/go.mod h1:0tlktQL7yHfYEtjcRGi/eiOkbDR5XF7gyFFvbC5//E0=
|
||||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
@ -480,9 +473,9 @@ github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQL
|
|||||||
github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
|
github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
|
||||||
github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
|
github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
|
||||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
|
||||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
@ -583,8 +576,8 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe
|
|||||||
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
|
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
|
||||||
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
|
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
|
||||||
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
|
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/gocql/gocql v0.0.0-20210515062232-b7ef815b4556/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY=
|
github.com/gocql/gocql v0.0.0-20210515062232-b7ef815b4556/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY=
|
||||||
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||||
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||||
@ -694,9 +687,7 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe
|
|||||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
|
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||||
github.com/google/pprof v0.0.0-20230309165930-d61513b1440d h1:um9/pc7tKMINFfP1eE7Wv6PRGXlcCSJkVajF7KJw3uQ=
|
|
||||||
github.com/google/pprof v0.0.0-20230309165930-d61513b1440d/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
|
|
||||||
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.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
@ -767,19 +758,15 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p
|
|||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
|
|
||||||
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
|
||||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||||
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-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
|
||||||
github.com/igm/sockjs-go/v3 v3.0.2 h1:2m0k53w0DBiGozeQUIEPR6snZFmpFpYvVsGnfLPNXbE=
|
github.com/igm/sockjs-go/v3 v3.0.2 h1:2m0k53w0DBiGozeQUIEPR6snZFmpFpYvVsGnfLPNXbE=
|
||||||
github.com/igm/sockjs-go/v3 v3.0.2/go.mod h1:UqchsOjeagIBFHvd+RZpLaVRbCwGilEC08EDHsD1jYE=
|
github.com/igm/sockjs-go/v3 v3.0.2/go.mod h1:UqchsOjeagIBFHvd+RZpLaVRbCwGilEC08EDHsD1jYE=
|
||||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
|
||||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ=
|
github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ=
|
||||||
@ -913,11 +900,10 @@ github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJG
|
|||||||
github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
|
github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
|
||||||
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
|
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
|
||||||
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
|
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
|
||||||
github.com/lestrrat-go/jwx/v2 v2.0.9 h1:TRX4Q630UXxPVLvP5vGaqVJO7S+0PE6msRZUsFSBoC8=
|
github.com/lestrrat-go/jwx/v2 v2.0.8 h1:jCFT8oc0hEDVjgUgsBy1F9cbjsjAVZSXNi7JaU9HR/Q=
|
||||||
github.com/lestrrat-go/jwx/v2 v2.0.9/go.mod h1:K68euYaR95FnL0hIQB8VvzL70vB7pSifbJUydCTPmgM=
|
github.com/lestrrat-go/jwx/v2 v2.0.8/go.mod h1:zLxnyv9rTlEvOUHbc48FAfIL8iYu2hHvIRaTFGc8mT0=
|
||||||
|
github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
|
||||||
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||||
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
|
|
||||||
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
@ -927,8 +913,6 @@ github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
|||||||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
||||||
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
|
github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
|
||||||
github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c=
|
|
||||||
github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y=
|
|
||||||
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
|
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
@ -946,7 +930,7 @@ github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcncea
|
|||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
|
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
|
||||||
@ -958,7 +942,6 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
|||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
@ -983,14 +966,10 @@ github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW
|
|||||||
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||||
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
|
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
|
|
||||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
|
||||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
|
||||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
@ -998,8 +977,6 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
|
|||||||
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/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
||||||
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
|
|
||||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
|
||||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||||
github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
|
github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
|
||||||
@ -1182,6 +1159,8 @@ github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2u
|
|||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
||||||
github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
||||||
|
github.com/santhosh-tekuri/jsonschema/v5 v5.1.1 h1:lEOLY2vyGIqKWUI9nzsOJRV3mb3WC9dXYORsLEUcoeY=
|
||||||
|
github.com/santhosh-tekuri/jsonschema/v5 v5.1.1/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0=
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||||
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
|
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
|
||||||
@ -1193,7 +1172,6 @@ github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
|||||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||||
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
|
||||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||||
@ -1218,8 +1196,6 @@ github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTd
|
|||||||
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
|
||||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
|
||||||
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||||
@ -1361,7 +1337,6 @@ go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
|||||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||||
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
@ -1395,9 +1370,9 @@ golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWP
|
|||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/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-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
@ -1515,6 +1490,7 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
@ -1523,9 +1499,8 @@ golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug
|
|||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/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-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
@ -1616,6 +1591,7 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -1681,16 +1657,14 @@ golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
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.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
@ -1698,9 +1672,8 @@ golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuX
|
|||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||||
|
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
|
||||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
|
||||||
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=
|
||||||
@ -1710,12 +1683,10 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
|
||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/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-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=
|
||||||
|
@ -30,6 +30,7 @@ func (a *Agent) Run(ctx context.Context) error {
|
|||||||
ticker := time.NewTicker(a.interval)
|
ticker := time.NewTicker(a.interval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
logger.Info(ctx, "generating token")
|
||||||
token, err := agent.GenerateToken(a.privateKey, a.thumbprint)
|
token, err := agent.GenerateToken(a.privateKey, a.thumbprint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
@ -39,32 +40,28 @@ func (a *Agent) Run(ctx context.Context) error {
|
|||||||
|
|
||||||
ctx = withClient(ctx, client)
|
ctx = withClient(ctx, client)
|
||||||
|
|
||||||
tick := func() {
|
|
||||||
logger.Debug(ctx, "registering agent")
|
|
||||||
|
|
||||||
if err := a.registerAgent(ctx, client, state); err != nil {
|
|
||||||
logger.Error(ctx, "could not register agent", logger.E(errors.WithStack(err)))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Debug(ctx, "state before reconciliation", logger.F("state", state))
|
|
||||||
|
|
||||||
if err := a.Reconcile(ctx, state); err != nil {
|
|
||||||
logger.Error(ctx, "could not reconcile node with state", logger.E(errors.WithStack(err)))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Debug(ctx, "state after reconciliation", logger.F("state", state))
|
|
||||||
}
|
|
||||||
|
|
||||||
tick()
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
tick()
|
|
||||||
|
logger.Debug(ctx, "registering agent")
|
||||||
|
|
||||||
|
if err := a.registerAgent(ctx, client, state); err != nil {
|
||||||
|
logger.Error(ctx, "could not register agent", logger.E(errors.WithStack(err)))
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug(ctx, "state before reconciliation", logger.F("state", state))
|
||||||
|
|
||||||
|
if err := a.Reconcile(ctx, state); err != nil {
|
||||||
|
logger.Error(ctx, "could not reconcile node with state", logger.E(errors.WithStack(err)))
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug(ctx, "state after reconciliation", logger.F("state", state))
|
||||||
|
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return errors.WithStack(ctx.Err())
|
return errors.WithStack(ctx.Err())
|
||||||
}
|
}
|
||||||
|
@ -1,190 +0,0 @@
|
|||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/app/spec"
|
|
||||||
appSpec "forge.cadoles.com/Cadoles/emissary/internal/agent/controller/app/spec"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/jwk"
|
|
||||||
"forge.cadoles.com/arcad/edge/pkg/app"
|
|
||||||
"forge.cadoles.com/arcad/edge/pkg/bus"
|
|
||||||
"forge.cadoles.com/arcad/edge/pkg/bus/memory"
|
|
||||||
edgeHTTP "forge.cadoles.com/arcad/edge/pkg/http"
|
|
||||||
"forge.cadoles.com/arcad/edge/pkg/module"
|
|
||||||
appModule "forge.cadoles.com/arcad/edge/pkg/module/app"
|
|
||||||
"forge.cadoles.com/arcad/edge/pkg/module/auth"
|
|
||||||
"forge.cadoles.com/arcad/edge/pkg/module/blob"
|
|
||||||
"forge.cadoles.com/arcad/edge/pkg/module/cast"
|
|
||||||
"forge.cadoles.com/arcad/edge/pkg/module/net"
|
|
||||||
"forge.cadoles.com/arcad/edge/pkg/storage/sqlite"
|
|
||||||
"github.com/Masterminds/sprig/v3"
|
|
||||||
"github.com/dop251/goja"
|
|
||||||
"github.com/lestrrat-go/jwx/v2/jwa"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
const defaultSQLiteParams = "?_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)&_txlock=immediate"
|
|
||||||
|
|
||||||
func (c *Controller) getHandlerOptions(ctx context.Context, appKey string, specs *spec.Spec) ([]edgeHTTP.HandlerOptionFunc, error) {
|
|
||||||
dataDir, err := c.ensureAppDataDir(ctx, appKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "could not retrieve app data dir")
|
|
||||||
}
|
|
||||||
|
|
||||||
dbFile := filepath.Join(dataDir, appKey+".sqlite")
|
|
||||||
db, err := sqlite.Open(dbFile + defaultSQLiteParams)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "could not open database file '%s'", dbFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
keySet, err := getAuthKeySet(specs.Config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "could not retrieve auth key set")
|
|
||||||
}
|
|
||||||
|
|
||||||
bundles := make([]string, 0, len(specs.Apps))
|
|
||||||
for appKey, app := range specs.Apps {
|
|
||||||
path := c.getAppBundlePath(appKey, app.Format)
|
|
||||||
bundles = append(bundles, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
bus := memory.NewBus()
|
|
||||||
modules := c.getAppModules(bus, db, specs, keySet)
|
|
||||||
|
|
||||||
options := []edgeHTTP.HandlerOptionFunc{
|
|
||||||
edgeHTTP.WithBus(bus),
|
|
||||||
edgeHTTP.WithServerModules(modules...),
|
|
||||||
}
|
|
||||||
|
|
||||||
return options, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAuthKeySet(config *spec.Config) (jwk.Set, error) {
|
|
||||||
keySet := jwk.NewSet()
|
|
||||||
if config == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
auth := config.Auth
|
|
||||||
|
|
||||||
if auth == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case auth.Local != nil:
|
|
||||||
var (
|
|
||||||
key jwk.Key
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
switch typedKey := auth.Local.Key.(type) {
|
|
||||||
case string:
|
|
||||||
key, err = jwk.FromRaw([]byte(typedKey))
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "could not parse local auth key")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := key.Set(jwk.AlgorithmKey, jwa.HS256); err != nil {
|
|
||||||
return nil, errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil, errors.Errorf("unexpected key type '%T'", auth.Local.Key)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := keySet.AddKey(key); err != nil {
|
|
||||||
return nil, errors.WithStack(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return keySet, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createGetAppURL(specs *spec.Spec) GetURLFunc {
|
|
||||||
var (
|
|
||||||
compileOnce sync.Once
|
|
||||||
urlTemplate *template.Template
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
return func(ctx context.Context, manifest *app.Manifest) (string, error) {
|
|
||||||
if err != nil {
|
|
||||||
return "", errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var appURLTemplate string
|
|
||||||
|
|
||||||
if specs.Config == nil || specs.Config.AppURLTemplate == "" {
|
|
||||||
appURLTemplate = `http://{{ last ( splitList "." ( toString .Manifest.ID ) ) }}.local`
|
|
||||||
} else {
|
|
||||||
appURLTemplate = specs.Config.AppURLTemplate
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOnce.Do(func() {
|
|
||||||
urlTemplate, err = template.New("").Funcs(sprig.TxtFuncMap()).Parse(appURLTemplate)
|
|
||||||
})
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
|
|
||||||
data := struct {
|
|
||||||
Manifest *app.Manifest
|
|
||||||
Specs *spec.Spec
|
|
||||||
}{
|
|
||||||
Manifest: manifest,
|
|
||||||
Specs: specs,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := urlTemplate.Execute(&buf, data); err != nil {
|
|
||||||
return "", errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String(), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Controller) getAppModules(bus bus.Bus, db *sql.DB, spec *appSpec.Spec, keySet jwk.Set) []app.ServerModuleFactory {
|
|
||||||
ds := sqlite.NewDocumentStoreWithDB(db)
|
|
||||||
bs := sqlite.NewBlobStoreWithDB(db)
|
|
||||||
|
|
||||||
return []app.ServerModuleFactory{
|
|
||||||
module.ContextModuleFactory(),
|
|
||||||
module.ConsoleModuleFactory(),
|
|
||||||
cast.CastModuleFactory(),
|
|
||||||
module.LifecycleModuleFactory(),
|
|
||||||
net.ModuleFactory(bus),
|
|
||||||
module.RPCModuleFactory(bus),
|
|
||||||
module.StoreModuleFactory(ds),
|
|
||||||
blob.ModuleFactory(bus, bs),
|
|
||||||
module.Extends(
|
|
||||||
auth.ModuleFactory(
|
|
||||||
auth.WithJWT(func() (jwk.Set, error) {
|
|
||||||
return keySet, nil
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
func(o *goja.Object) {
|
|
||||||
if err := o.Set("CLAIM_TENANT", "arcad_tenant"); err != nil {
|
|
||||||
panic(errors.New("could not set 'CLAIM_TENANT' property"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := o.Set("CLAIM_ENTRYPOINT", "arcad_entrypoint"); err != nil {
|
|
||||||
panic(errors.New("could not set 'CLAIM_ENTRYPOINT' property"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := o.Set("CLAIM_ROLE", "arcad_role"); err != nil {
|
|
||||||
panic(errors.New("could not set 'CLAIM_ROLE' property"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := o.Set("CLAIM_PREFERRED_USERNAME", "preferred_username"); err != nil {
|
|
||||||
panic(errors.New("could not set 'CLAIM_PREFERRED_USERNAME' property"))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
appModule.ModuleFactory(c.appRepository),
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"forge.cadoles.com/arcad/edge/pkg/app"
|
|
||||||
"forge.cadoles.com/arcad/edge/pkg/bundle"
|
|
||||||
appModule "forge.cadoles.com/arcad/edge/pkg/module/app"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
type GetURLFunc func(context.Context, *app.Manifest) (string, error)
|
|
||||||
|
|
||||||
type AppRepository struct {
|
|
||||||
getURL GetURLFunc
|
|
||||||
bundles []string
|
|
||||||
mutex sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get implements app.Repository
|
|
||||||
func (r *AppRepository) Get(ctx context.Context, id app.ID) (*app.Manifest, error) {
|
|
||||||
r.mutex.RLock()
|
|
||||||
defer r.mutex.RUnlock()
|
|
||||||
|
|
||||||
manifest, err := r.findManifest(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return manifest, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetURL implements app.Repository
|
|
||||||
func (r *AppRepository) GetURL(ctx context.Context, id app.ID) (string, error) {
|
|
||||||
r.mutex.RLock()
|
|
||||||
defer r.mutex.RUnlock()
|
|
||||||
|
|
||||||
manifest, err := r.findManifest(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return "", errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
url, err := r.getURL(ctx, manifest)
|
|
||||||
if err != nil {
|
|
||||||
return "", errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return url, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// List implements app.Repository
|
|
||||||
func (r *AppRepository) List(ctx context.Context) ([]*app.Manifest, error) {
|
|
||||||
r.mutex.RLock()
|
|
||||||
defer r.mutex.RUnlock()
|
|
||||||
|
|
||||||
manifests := make([]*app.Manifest, 0)
|
|
||||||
|
|
||||||
for _, path := range r.bundles {
|
|
||||||
bundleCtx := logger.With(ctx, logger.F("path", path))
|
|
||||||
|
|
||||||
bundle, err := bundle.FromPath(path)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(bundleCtx, "could not load bundle", logger.E(errors.WithStack(err)))
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
manifest, err := app.LoadManifest(bundle)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(bundleCtx, "could not load manifest", logger.E(errors.WithStack(err)))
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
manifests = append(manifests, manifest)
|
|
||||||
}
|
|
||||||
|
|
||||||
return manifests, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *AppRepository) Update(getURL GetURLFunc, bundles []string) {
|
|
||||||
r.mutex.Lock()
|
|
||||||
defer r.mutex.Unlock()
|
|
||||||
|
|
||||||
r.getURL = getURL
|
|
||||||
r.bundles = bundles
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *AppRepository) findManifest(ctx context.Context, id app.ID) (*app.Manifest, error) {
|
|
||||||
for _, path := range r.bundles {
|
|
||||||
bundleCtx := logger.With(ctx, logger.F("path", path))
|
|
||||||
|
|
||||||
bundle, err := bundle.FromPath(path)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(bundleCtx, "could not load bundle", logger.E(errors.WithStack(err)))
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
manifest, err := app.LoadManifest(bundle)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(bundleCtx, "could not load manifest", logger.E(errors.WithStack(err)))
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if manifest.ID != id {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return manifest, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.WithStack(appModule.ErrNotFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAppRepository() *AppRepository {
|
|
||||||
return &AppRepository{
|
|
||||||
getURL: func(ctx context.Context, m *app.Manifest) (string, error) {
|
|
||||||
return "", errors.New("unavailable")
|
|
||||||
},
|
|
||||||
bundles: []string{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ appModule.Repository = &AppRepository{}
|
|
@ -8,24 +8,19 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent"
|
"forge.cadoles.com/Cadoles/emissary/internal/agent"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/app/spec"
|
"forge.cadoles.com/Cadoles/emissary/internal/spec/app"
|
||||||
"forge.cadoles.com/arcad/edge/pkg/bundle"
|
"forge.cadoles.com/arcad/edge/pkg/bundle"
|
||||||
"github.com/mitchellh/hashstructure/v2"
|
"forge.cadoles.com/arcad/edge/pkg/storage/sqlite"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type serverEntry struct {
|
|
||||||
AppDefHash uint64
|
|
||||||
Server *Server
|
|
||||||
}
|
|
||||||
|
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
client *http.Client
|
currentSpecRevision int
|
||||||
downloadDir string
|
client *http.Client
|
||||||
dataDir string
|
downloadDir string
|
||||||
servers map[string]*serverEntry
|
dataDir string
|
||||||
appRepository *AppRepository
|
servers map[string]*Server
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name implements node.Controller.
|
// Name implements node.Controller.
|
||||||
@ -35,13 +30,13 @@ func (c *Controller) Name() string {
|
|||||||
|
|
||||||
// Reconcile implements node.Controller.
|
// Reconcile implements node.Controller.
|
||||||
func (c *Controller) Reconcile(ctx context.Context, state *agent.State) error {
|
func (c *Controller) Reconcile(ctx context.Context, state *agent.State) error {
|
||||||
appSpec := spec.NewSpec()
|
appSpec := app.NewSpec()
|
||||||
|
|
||||||
if err := state.GetSpec(spec.Name, appSpec); err != nil {
|
if err := state.GetSpec(app.NameApp, appSpec); err != nil {
|
||||||
if errors.Is(err, agent.ErrSpecNotFound) {
|
if errors.Is(err, agent.ErrSpecNotFound) {
|
||||||
logger.Info(ctx, "could not find app spec")
|
logger.Info(ctx, "could not find app spec, stopping all remaining apps")
|
||||||
|
|
||||||
c.stopAllApps(ctx, appSpec)
|
c.stopAllApps(ctx)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -51,20 +46,22 @@ func (c *Controller) Reconcile(ctx context.Context, state *agent.State) error {
|
|||||||
|
|
||||||
logger.Info(ctx, "retrieved spec", logger.F("spec", appSpec.SpecName()), logger.F("revision", appSpec.SpecRevision()))
|
logger.Info(ctx, "retrieved spec", logger.F("spec", appSpec.SpecName()), logger.F("revision", appSpec.SpecRevision()))
|
||||||
|
|
||||||
|
if c.currentSpecRevision == appSpec.SpecRevision() {
|
||||||
|
logger.Info(ctx, "spec revision did not change, doing nothing")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
c.updateApps(ctx, appSpec)
|
c.updateApps(ctx, appSpec)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) stopAllApps(ctx context.Context, spec *spec.Spec) {
|
func (c *Controller) stopAllApps(ctx context.Context) {
|
||||||
if len(c.servers) > 0 {
|
for appID, server := range c.servers {
|
||||||
logger.Info(ctx, "stopping all apps")
|
|
||||||
}
|
|
||||||
|
|
||||||
for appID, entry := range c.servers {
|
|
||||||
logger.Info(ctx, "stopping app", logger.F("appID", appID))
|
logger.Info(ctx, "stopping app", logger.F("appID", appID))
|
||||||
|
|
||||||
if err := entry.Server.Stop(); err != nil {
|
if err := server.Stop(); err != nil {
|
||||||
logger.Error(
|
logger.Error(
|
||||||
ctx, "error while stopping app",
|
ctx, "error while stopping app",
|
||||||
logger.F("appID", appID),
|
logger.F("appID", appID),
|
||||||
@ -76,88 +73,71 @@ func (c *Controller) stopAllApps(ctx context.Context, spec *spec.Spec) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) updateApps(ctx context.Context, specs *spec.Spec) {
|
func (c *Controller) updateApps(ctx context.Context, spec *app.Spec) {
|
||||||
|
hadError := false
|
||||||
|
|
||||||
// Stop and remove obsolete apps
|
// Stop and remove obsolete apps
|
||||||
for appKey, server := range c.servers {
|
for appID, server := range c.servers {
|
||||||
if _, exists := specs.Apps[appKey]; exists {
|
if _, exists := spec.Apps[appID]; exists {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info(ctx, "stopping app", logger.F("appKey", appKey))
|
logger.Info(ctx, "stopping app", logger.F("appID", appID))
|
||||||
|
|
||||||
if err := server.Server.Stop(); err != nil {
|
if err := server.Stop(); err != nil {
|
||||||
logger.Error(
|
logger.Error(
|
||||||
ctx, "error while stopping app",
|
ctx, "error while stopping app",
|
||||||
logger.F("appKey", appKey),
|
logger.F("gatewayID", appID),
|
||||||
logger.E(errors.WithStack(err)),
|
logger.E(errors.WithStack(err)),
|
||||||
)
|
)
|
||||||
|
|
||||||
delete(c.servers, appKey)
|
delete(c.servers, appID)
|
||||||
|
|
||||||
|
hadError = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.updateAppRepository(ctx, specs)
|
// (Re)start apps
|
||||||
|
for appID, appSpec := range spec.Apps {
|
||||||
|
appCtx := logger.With(ctx, logger.F("appID", appID))
|
||||||
|
|
||||||
// (Re)start apps if necessary
|
if err := c.updateApp(ctx, appID, appSpec); err != nil {
|
||||||
for appKey := range specs.Apps {
|
|
||||||
appCtx := logger.With(ctx, logger.F("appKey", appKey))
|
|
||||||
|
|
||||||
if err := c.updateApp(ctx, specs, appKey); err != nil {
|
|
||||||
logger.Error(appCtx, "could not update app", logger.E(errors.WithStack(err)))
|
logger.Error(appCtx, "could not update app", logger.E(errors.WithStack(err)))
|
||||||
|
|
||||||
|
hadError = true
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !hadError {
|
||||||
|
c.currentSpecRevision = spec.SpecRevision()
|
||||||
|
logger.Info(ctx, "updating current spec revision", logger.F("revision", c.currentSpecRevision))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) updateAppRepository(ctx context.Context, specs *spec.Spec) {
|
func (c *Controller) updateApp(ctx context.Context, appID string, appSpec app.AppEntry) error {
|
||||||
bundles := make([]string, 0, len(specs.Apps))
|
bundle, sha256sum, err := c.ensureAppBundle(ctx, appID, appSpec)
|
||||||
for appKey, app := range specs.Apps {
|
|
||||||
path := c.getAppBundlePath(appKey, app.Format)
|
|
||||||
bundles = append(bundles, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
getURL := createGetAppURL(specs)
|
|
||||||
|
|
||||||
c.appRepository.Update(getURL, bundles)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Controller) updateApp(ctx context.Context, specs *spec.Spec, appKey string) (err error) {
|
|
||||||
appEntry := specs.Apps[appKey]
|
|
||||||
|
|
||||||
var auth *spec.Auth
|
|
||||||
if specs.Config != nil {
|
|
||||||
auth = specs.Config.Auth
|
|
||||||
}
|
|
||||||
|
|
||||||
appDef := struct {
|
|
||||||
App spec.AppEntry
|
|
||||||
Auth *spec.Auth
|
|
||||||
}{
|
|
||||||
App: appEntry,
|
|
||||||
Auth: auth,
|
|
||||||
}
|
|
||||||
|
|
||||||
newAppDefHash, err := hashstructure.Hash(appDef, hashstructure.FormatV2, nil)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bundle, sha256sum, err := c.ensureAppBundle(ctx, appKey, appEntry)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "could not download app bundle")
|
return errors.Wrap(err, "could not download app bundle")
|
||||||
}
|
}
|
||||||
|
|
||||||
server, exists := c.servers[appKey]
|
dataDir, err := c.ensureAppDataDir(ctx, appID)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not retrieve app data dir")
|
||||||
|
}
|
||||||
|
|
||||||
|
server, exists := c.servers[appID]
|
||||||
if !exists {
|
if !exists {
|
||||||
logger.Info(ctx, "app currently not running")
|
logger.Info(ctx, "app currently not running")
|
||||||
} else if sha256sum != appEntry.SHA256Sum {
|
} else if sha256sum != appSpec.SHA256Sum {
|
||||||
logger.Info(
|
logger.Info(
|
||||||
ctx, "bundle hash mismatch, stopping app",
|
ctx, "bundle hash mismatch, stopping app",
|
||||||
logger.F("currentHash", sha256sum),
|
logger.F("currentHash", sha256sum),
|
||||||
logger.F("specHash", appEntry.SHA256Sum),
|
logger.F("specHash", appSpec.SHA256Sum),
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := server.Server.Stop(); err != nil {
|
if err := server.Stop(); err != nil {
|
||||||
return errors.Wrap(err, "could not stop app")
|
return errors.Wrap(err, "could not stop app")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,59 +145,36 @@ func (c *Controller) updateApp(ctx context.Context, specs *spec.Spec, appKey str
|
|||||||
}
|
}
|
||||||
|
|
||||||
if server == nil {
|
if server == nil {
|
||||||
options, err := c.getHandlerOptions(ctx, appKey, specs)
|
dbFile := filepath.Join(dataDir, appID+".sqlite")
|
||||||
|
db, err := sqlite.Open(dbFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "could not create handler options")
|
return errors.Wrapf(err, "could not opend database file '%s'", dbFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
var auth *spec.Auth
|
server = NewServer(bundle, db)
|
||||||
if specs.Config != nil {
|
c.servers[appID] = server
|
||||||
auth = specs.Config.Auth
|
|
||||||
}
|
|
||||||
|
|
||||||
server = &serverEntry{
|
|
||||||
Server: NewServer(bundle, auth, options...),
|
|
||||||
AppDefHash: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
c.servers[appKey] = server
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defChanged := newAppDefHash != server.AppDefHash
|
logger.Info(
|
||||||
|
ctx, "starting app",
|
||||||
|
logger.F("address", appSpec.Address),
|
||||||
|
)
|
||||||
|
|
||||||
if server.Server.Running() && !defChanged {
|
if err := server.Start(ctx, appSpec.Address); err != nil {
|
||||||
return nil
|
delete(c.servers, appID)
|
||||||
}
|
|
||||||
|
|
||||||
if defChanged && server.AppDefHash != 0 {
|
|
||||||
logger.Info(
|
|
||||||
ctx, "restarting app",
|
|
||||||
logger.F("address", appEntry.Address),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
logger.Info(
|
|
||||||
ctx, "starting app",
|
|
||||||
logger.F("address", appEntry.Address),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := server.Server.Start(ctx, appEntry.Address); err != nil {
|
|
||||||
delete(c.servers, appKey)
|
|
||||||
|
|
||||||
return errors.Wrap(err, "could not start app")
|
return errors.Wrap(err, "could not start app")
|
||||||
}
|
}
|
||||||
|
|
||||||
server.AppDefHash = newAppDefHash
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) ensureAppBundle(ctx context.Context, appID string, spec spec.AppEntry) (bundle.Bundle, string, error) {
|
func (c *Controller) ensureAppBundle(ctx context.Context, appID string, spec app.AppEntry) (bundle.Bundle, string, error) {
|
||||||
if err := os.MkdirAll(c.downloadDir, os.ModePerm); err != nil {
|
if err := os.MkdirAll(c.downloadDir, os.ModePerm); err != nil {
|
||||||
return nil, "", errors.WithStack(err)
|
return nil, "", errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bundlePath := c.getAppBundlePath(appID, spec.Format)
|
bundlePath := filepath.Join(c.downloadDir, appID+"."+spec.Format)
|
||||||
|
|
||||||
_, err := os.Stat(bundlePath)
|
_, err := os.Stat(bundlePath)
|
||||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
@ -311,10 +268,6 @@ func (c *Controller) ensureAppDataDir(ctx context.Context, appID string) (string
|
|||||||
return dataDir, nil
|
return dataDir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) getAppBundlePath(appKey string, format string) string {
|
|
||||||
return filepath.Join(c.downloadDir, appKey+"."+format)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewController(funcs ...OptionFunc) *Controller {
|
func NewController(funcs ...OptionFunc) *Controller {
|
||||||
opts := defaultOptions()
|
opts := defaultOptions()
|
||||||
for _, fn := range funcs {
|
for _, fn := range funcs {
|
||||||
@ -322,11 +275,11 @@ func NewController(funcs ...OptionFunc) *Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &Controller{
|
return &Controller{
|
||||||
client: opts.Client,
|
client: opts.Client,
|
||||||
downloadDir: opts.DownloadDir,
|
downloadDir: opts.DownloadDir,
|
||||||
dataDir: opts.DataDir,
|
dataDir: opts.DataDir,
|
||||||
servers: make(map[string]*serverEntry),
|
currentSpecRevision: -1,
|
||||||
appRepository: NewAppRepository(),
|
servers: make(map[string]*Server),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,61 +2,54 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/app/spec"
|
"forge.cadoles.com/arcad/edge/pkg/app"
|
||||||
appSpec "forge.cadoles.com/Cadoles/emissary/internal/agent/controller/app/spec"
|
"forge.cadoles.com/arcad/edge/pkg/bus"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/proxy/wildcard"
|
"forge.cadoles.com/arcad/edge/pkg/bus/memory"
|
||||||
edgeHTTP "forge.cadoles.com/arcad/edge/pkg/http"
|
edgeHTTP "forge.cadoles.com/arcad/edge/pkg/http"
|
||||||
authHTTP "forge.cadoles.com/arcad/edge/pkg/module/auth/http"
|
"forge.cadoles.com/arcad/edge/pkg/module"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
"forge.cadoles.com/arcad/edge/pkg/module/cast"
|
||||||
|
"forge.cadoles.com/arcad/edge/pkg/module/net"
|
||||||
|
"forge.cadoles.com/arcad/edge/pkg/storage"
|
||||||
|
"forge.cadoles.com/arcad/edge/pkg/storage/sqlite"
|
||||||
|
|
||||||
"forge.cadoles.com/arcad/edge/pkg/bundle"
|
"forge.cadoles.com/arcad/edge/pkg/bundle"
|
||||||
"github.com/go-chi/chi/middleware"
|
"github.com/go-chi/chi/middleware"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/lestrrat-go/jwx/v2/jwa"
|
|
||||||
"github.com/lestrrat-go/jwx/v2/jwk"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/imports/passwd"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultCookieDuration time.Duration = 24 * time.Hour
|
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
bundle bundle.Bundle
|
bundle bundle.Bundle
|
||||||
handlerOptions []edgeHTTP.HandlerOptionFunc
|
db *sql.DB
|
||||||
server *http.Server
|
server *http.Server
|
||||||
serverMutex sync.RWMutex
|
|
||||||
auth *appSpec.Auth
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Start(ctx context.Context, addr string) (err error) {
|
func (s *Server) Start(ctx context.Context, addr string) error {
|
||||||
if s.Running() {
|
if s.server != nil {
|
||||||
if err := s.Stop(); err != nil {
|
if err := s.Stop(); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.serverMutex.Lock()
|
|
||||||
defer s.serverMutex.Unlock()
|
|
||||||
|
|
||||||
router := chi.NewRouter()
|
router := chi.NewRouter()
|
||||||
|
|
||||||
router.Use(middleware.Logger)
|
router.Use(middleware.Logger)
|
||||||
|
|
||||||
handler := edgeHTTP.NewHandler(s.handlerOptions...)
|
bus := memory.NewBus()
|
||||||
|
ds := sqlite.NewDocumentStoreWithDB(s.db)
|
||||||
|
bs := sqlite.NewBlobStoreWithDB(s.db)
|
||||||
|
|
||||||
|
handler := edgeHTTP.NewHandler(
|
||||||
|
edgeHTTP.WithBus(bus),
|
||||||
|
edgeHTTP.WithServerModules(s.getAppModules(bus, ds, bs)...),
|
||||||
|
)
|
||||||
if err := handler.Load(s.bundle); err != nil {
|
if err := handler.Load(s.bundle); err != nil {
|
||||||
return errors.Wrap(err, "could not load app bundle")
|
return errors.Wrap(err, "could not load app bundle")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.configureAuth(router, s.auth); err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
router.Handle("/*", handler)
|
router.Handle("/*", handler)
|
||||||
|
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
@ -65,18 +58,6 @@ func (s *Server) Start(ctx context.Context, addr string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
|
||||||
if recovered := recover(); recovered != nil {
|
|
||||||
if err, ok := recovered.(error); ok {
|
|
||||||
logger.Error(ctx, err.Error(), logger.E(errors.WithStack(err)))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
panic(recovered)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := s.Stop(); err != nil {
|
if err := s.Stop(); err != nil {
|
||||||
panic(errors.WithStack(err))
|
panic(errors.WithStack(err))
|
||||||
@ -93,110 +74,52 @@ func (s *Server) Start(ctx context.Context, addr string) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Running() bool {
|
|
||||||
s.serverMutex.RLock()
|
|
||||||
defer s.serverMutex.RUnlock()
|
|
||||||
|
|
||||||
return s.server != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) Stop() error {
|
func (s *Server) Stop() error {
|
||||||
if !s.Running() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
s.serverMutex.Lock()
|
|
||||||
defer s.serverMutex.Unlock()
|
|
||||||
|
|
||||||
if s.server == nil {
|
if s.server == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.server.Close(); err != nil {
|
defer func() {
|
||||||
s.server = nil
|
s.server = nil
|
||||||
|
}()
|
||||||
|
|
||||||
return errors.WithStack(err)
|
if err := s.server.Close(); err != nil {
|
||||||
}
|
panic(errors.WithStack(err))
|
||||||
|
|
||||||
s.server = nil
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) configureAuth(router chi.Router, auth *spec.Auth) error {
|
|
||||||
if auth == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case auth.Local != nil:
|
|
||||||
var rawKey any = s.auth.Local.Key
|
|
||||||
if strKey, ok := rawKey.(string); ok {
|
|
||||||
rawKey = []byte(strKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := jwk.FromRaw(rawKey)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cookieDuration := defaultCookieDuration
|
|
||||||
if s.auth.Local.CookieDuration != "" {
|
|
||||||
cookieDuration, err = time.ParseDuration(s.auth.Local.CookieDuration)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.auth.Local.CookieDomain != "" {
|
|
||||||
router.Use(invalidCookieDomainRedirect(s.auth.Local.CookieDomain))
|
|
||||||
}
|
|
||||||
|
|
||||||
router.Handle("/auth/*", authHTTP.NewLocalHandler(
|
|
||||||
jwa.HS256, key,
|
|
||||||
authHTTP.WithRoutePrefix("/auth"),
|
|
||||||
authHTTP.WithAccounts(s.auth.Local.Accounts...),
|
|
||||||
authHTTP.WithCookieOptions(s.auth.Local.CookieDomain, cookieDuration),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(bundle bundle.Bundle, auth *appSpec.Auth, handlerOptions ...edgeHTTP.HandlerOptionFunc) *Server {
|
func (s *Server) getAppModules(bus bus.Bus, ds storage.DocumentStore, bs storage.BlobStore) []app.ServerModuleFactory {
|
||||||
|
return []app.ServerModuleFactory{
|
||||||
|
module.ContextModuleFactory(),
|
||||||
|
module.ConsoleModuleFactory(),
|
||||||
|
cast.CastModuleFactory(),
|
||||||
|
module.LifecycleModuleFactory(),
|
||||||
|
net.ModuleFactory(bus),
|
||||||
|
module.RPCModuleFactory(bus),
|
||||||
|
module.StoreModuleFactory(ds),
|
||||||
|
module.BlobModuleFactory(bus, bs),
|
||||||
|
// module.Extends(
|
||||||
|
// auth.ModuleFactory(
|
||||||
|
// auth.WithJWT(dummyKeyFunc),
|
||||||
|
// ),
|
||||||
|
// func(o *goja.Object) {
|
||||||
|
// if err := o.Set("CLAIM_ROLE", "role"); err != nil {
|
||||||
|
// panic(errors.New("could not set 'CLAIM_ROLE' property"))
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if err := o.Set("CLAIM_PREFERRED_USERNAME", "preferred_username"); err != nil {
|
||||||
|
// panic(errors.New("could not set 'CLAIM_PREFERRED_USERNAME' property"))
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(bundle bundle.Bundle, db *sql.DB) *Server {
|
||||||
return &Server{
|
return &Server{
|
||||||
bundle: bundle,
|
bundle: bundle,
|
||||||
auth: auth,
|
db: db,
|
||||||
handlerOptions: handlerOptions,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func invalidCookieDomainRedirect(cookieDomain string) func(http.Handler) http.Handler {
|
|
||||||
domain := strings.TrimPrefix(cookieDomain, ".")
|
|
||||||
hostPattern := "*" + domain
|
|
||||||
|
|
||||||
return func(h http.Handler) http.Handler {
|
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
hostParts := strings.SplitN(r.Host, ":", 2)
|
|
||||||
|
|
||||||
if !wildcard.Match(hostParts[0], hostPattern) {
|
|
||||||
url := r.URL
|
|
||||||
|
|
||||||
newHost := domain
|
|
||||||
if len(hostParts) > 1 {
|
|
||||||
newHost += ":" + hostParts[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
url.Host = newHost
|
|
||||||
|
|
||||||
http.Redirect(w, r, url.String(), http.StatusTemporaryRedirect)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return http.HandlerFunc(fn)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
||||||
"$id": "https://app.edge.emissary.cadoles.com/spec.json",
|
|
||||||
"title": "AppSpec",
|
|
||||||
"description": "Emissary 'App' specification",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"apps": {
|
|
||||||
"type": "object",
|
|
||||||
"patternProperties": {
|
|
||||||
".*": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"url": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"sha256sum": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"address": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"format": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"zip",
|
|
||||||
"tar.gz"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"url",
|
|
||||||
"sha256sum",
|
|
||||||
"address",
|
|
||||||
"format"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"auth": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"local": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"key": {
|
|
||||||
"type": ["object", "string"]
|
|
||||||
},
|
|
||||||
"accounts": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"username": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"password": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"algo": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"claims": {
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"username",
|
|
||||||
"password",
|
|
||||||
"algo"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cookieDomain": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"cookieDuration": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"key"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"config": {
|
|
||||||
"appUrlTemplate": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"apps"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
package spec
|
|
||||||
|
|
||||||
import (
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
|
||||||
edgeAuth "forge.cadoles.com/arcad/edge/pkg/module/auth/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
const Name spec.Name = "app.emissary.cadoles.com"
|
|
||||||
|
|
||||||
type Spec struct {
|
|
||||||
Revision int `json:"revision"`
|
|
||||||
Apps map[string]AppEntry `json:"apps"`
|
|
||||||
Config *Config `json:"config"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AppEntry struct {
|
|
||||||
URL string `json:"url"`
|
|
||||||
SHA256Sum string `json:"sha256sum"`
|
|
||||||
Address string `json:"address"`
|
|
||||||
Format string `json:"format"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Auth struct {
|
|
||||||
Local *LocalAuth `json:"local,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type LocalAuth struct {
|
|
||||||
Key any `json:"key"`
|
|
||||||
Accounts []edgeAuth.LocalAccount `json:"accounts"`
|
|
||||||
CookieDomain string `json:"cookieDomain"`
|
|
||||||
CookieDuration string `json:"cookieDuration"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Auth *Auth `json:"auth"`
|
|
||||||
AppURLTemplate string `json:"appUrlTemplate"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Spec) SpecName() spec.Name {
|
|
||||||
return Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Spec) SpecRevision() int {
|
|
||||||
return s.Revision
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Spec) SpecData() map[string]any {
|
|
||||||
return map[string]any{
|
|
||||||
"apps": s.Apps,
|
|
||||||
"config": s.Config,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSpec() *Spec {
|
|
||||||
return &Spec{
|
|
||||||
Revision: -1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ spec.Spec = &Spec{}
|
|
@ -1,42 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "app.emissary.cadoles.com",
|
|
||||||
"data": {
|
|
||||||
"apps": {
|
|
||||||
"edge.sdk.client.test": {
|
|
||||||
"url": "http://example.com/edge.sdk.client.test_0.0.0.zip",
|
|
||||||
"sha256sum": "58019192dacdae17755707719707db007e26dac856102280583fbd18427dd352",
|
|
||||||
"address": ":8081",
|
|
||||||
"format": "zip"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"auth": {
|
|
||||||
"local": {
|
|
||||||
"key": {
|
|
||||||
"d": "YOre0WZefGfUGFvDg42oL5Oad5Zsb1N_hqPyLVM5ajpTZzcHpB3wT6In9tFO_VshB6lxVtPA9ckPkpMTFY7ygt1Yomc1HkoOKRtmIaqdr4VgNQifU-4yiLiJkSbdYSeMV-KkkN8mGR1keJpJeS34W1X0W6CkU2nw7F5VueBCJfWJA0funRfuWdI68MTUgT9kRZFp-SfvptvRL6jVYHV_5hqxzHCvgEdBSF6QKwx4M6P6QBMt7ft6uMLmFx9abKFw2V51hX3PkxiSepVB3w5CYg4HtS3AHX6bILL4m0R2pdTIkap7i3tkH_xAOuKWt8D6JhadI8X1rEAwXmCS5KrRgQ",
|
|
||||||
"dp": "U0HfvBC6hk-SCpuotGIv3vbHCVt1aF3SHK0y32EYCOe8e_9G6YCEILfcvEJ5fiOCc2kvx6TasHQu4qj1uWRKenZlK1sJ6KDybGCkZL1D3jYnbeLZYBuWBL__YbZiST3ewbxzj_EDMWiZ8sUltahza_1weSgg8auSzTHS2LJBHIE",
|
|
||||||
"dq": "hVom4ScDxgqhCsQNVpZlN7M3v0tgWjl_gTOHjOyzKCHQJeC0QmJJaMKkQZPWJ8jjLqy7VwVpqC2nZU7QDuX1Cq5eJDQcXi9XtaAfIBico9WcYDre6mDyhL588YHpekyRke8HnZ810iesr0G3gU1h0QvZVVuW-pXTJOXhZTt6nFc",
|
|
||||||
"e": "AQAB",
|
|
||||||
"kty": "RSA",
|
|
||||||
"n": "vPnpkE3-HfNgJSru_K40LstkjiG2Bq_Tt-m0d_yUBBSbirFxF3qH4EXi7WrtZdeDahg2iV2BvpbVVj9GlmGo9OLol6jc7AP2yvZrkbABiiJhCbuPdkYbNpx6B7Itl8RT_bUSYAMZhmux5lpsn4weQ01fzjICi1rA-bIJpOfotdOjP4_lol-LxGZOGJQv9kndP8bgmssJb3Y_2s4gPtkmXySLrhpr5So-_6dVksyuBD9aLcnsMLDbywusjEMCdhqzQbvOjryomnmEXwyz_Ewb5HFK2PfgFtoHkdjqDz-mrEs3tw5g4TdYhCftzJxgbyNAEq4aEiOQrAncYyrXlotP_w",
|
|
||||||
"p": "8TNMF0WUe7CEeNVUTsuEcBAAXRguNtpvVifIjlwzFRGOYVGIpKuHsqQPKlZL07I9gPr9LifQnyQus3oEmTOrVs6LB9sfbukbg43ZRKoGVM40JYF5Xjs7R3mEZhgU0WaYOVe3iLtBGMfXNWFwlbfQP-zEb-dPCBX1jWT3LdgNBcE",
|
|
||||||
"q": "yJJLNc9w6O4y2icME8k99FugV9E7ObwUxF3v5JN3y1cmAT0h2njyE3iAGqaDZwcY1_jGCisjwoqX6i5E8xqhxX3Gcy3J7SmUAf8fhY8wU3zv9DK7skg2IdvanDb8Y1OM6GchbYZAOVPEg2IvVio8zI-Ih3DDwDk8Df0ufzoHRb8",
|
|
||||||
"qi": "zOE-4R3cjPesm3MX-4PdwmsaF9QZLUVRUvvHJ08pKs6kAXP18hzjctAoOjhQDxlTYqNYNePfKzKwost3OJoPgRIc9w9qwUCK1gNOS4Z_xozCIaXgMddNFhkoAfZ4JaKjNCiinzjGfqG99Lf-yzmmREuuhRv7SdS3ST4VQjiJQew"
|
|
||||||
},
|
|
||||||
"accounts": [
|
|
||||||
{
|
|
||||||
"username": "foo",
|
|
||||||
"algo": "plain",
|
|
||||||
"password": "bar",
|
|
||||||
"claims": {
|
|
||||||
"arcad_role": "user",
|
|
||||||
"arcad_tenant": "dev.cli",
|
|
||||||
"preferred_username": "Foo",
|
|
||||||
"sub": "foo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"revision": 0
|
|
||||||
}
|
|
124
internal/agent/controller/gateway/controller.go
Normal file
124
internal/agent/controller/gateway/controller.go
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/agent"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/spec/gateway"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Controller struct {
|
||||||
|
proxies map[gateway.ID]*ReverseProxy
|
||||||
|
currentSpecRevision int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name implements node.Controller.
|
||||||
|
func (c *Controller) Name() string {
|
||||||
|
return "gateway-controller"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reconcile implements node.Controller.
|
||||||
|
func (c *Controller) Reconcile(ctx context.Context, state *agent.State) error {
|
||||||
|
gatewaySpec := gateway.NewSpec()
|
||||||
|
|
||||||
|
if err := state.GetSpec(gateway.NameGateway, gatewaySpec); err != nil {
|
||||||
|
if errors.Is(err, agent.ErrSpecNotFound) {
|
||||||
|
logger.Info(ctx, "could not find gateway spec, stopping all remaining proxies")
|
||||||
|
|
||||||
|
c.stopAllProxies(ctx)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info(ctx, "retrieved spec", logger.F("spec", gatewaySpec.SpecName()), logger.F("revision", gatewaySpec.SpecRevision()))
|
||||||
|
|
||||||
|
if c.currentSpecRevision == gatewaySpec.SpecRevision() {
|
||||||
|
logger.Info(ctx, "spec revision did not change, doing nothing")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c.updateProxies(ctx, gatewaySpec)
|
||||||
|
|
||||||
|
c.currentSpecRevision = gatewaySpec.SpecRevision()
|
||||||
|
logger.Info(ctx, "updating current spec revision", logger.F("revision", c.currentSpecRevision))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) stopAllProxies(ctx context.Context) {
|
||||||
|
for gatewayID, proxy := range c.proxies {
|
||||||
|
logger.Info(ctx, "stopping proxy", logger.F("gatewayID", gatewayID))
|
||||||
|
|
||||||
|
if err := proxy.Stop(); err != nil {
|
||||||
|
logger.Error(
|
||||||
|
ctx, "error while stopping proxy",
|
||||||
|
logger.F("gatewayID", gatewayID),
|
||||||
|
logger.E(errors.WithStack(err)),
|
||||||
|
)
|
||||||
|
|
||||||
|
delete(c.proxies, gatewayID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) updateProxies(ctx context.Context, spec *gateway.Spec) {
|
||||||
|
// Stop and remove obsolete gateways
|
||||||
|
for gatewayID, proxy := range c.proxies {
|
||||||
|
if _, exists := spec.Gateways[gatewayID]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info(ctx, "stopping proxy", logger.F("gatewayID", gatewayID))
|
||||||
|
|
||||||
|
if err := proxy.Stop(); err != nil {
|
||||||
|
logger.Error(
|
||||||
|
ctx, "error while stopping proxy",
|
||||||
|
logger.F("gatewayID", gatewayID),
|
||||||
|
logger.E(errors.WithStack(err)),
|
||||||
|
)
|
||||||
|
|
||||||
|
delete(c.proxies, gatewayID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// (Re)start gateways
|
||||||
|
for gatewayID, gatewaySpec := range spec.Gateways {
|
||||||
|
proxy, exists := c.proxies[gatewayID]
|
||||||
|
if !exists {
|
||||||
|
proxy = NewReverseProxy()
|
||||||
|
c.proxies[gatewayID] = proxy
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info(
|
||||||
|
ctx, "starting proxy",
|
||||||
|
logger.F("gatewayID", gatewayID),
|
||||||
|
logger.F("addr", gatewaySpec.Address),
|
||||||
|
logger.F("target", gatewaySpec.Target),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := proxy.Start(ctx, gatewaySpec.Address, gatewaySpec.Target); err != nil {
|
||||||
|
logger.Error(
|
||||||
|
ctx, "error while starting proxy",
|
||||||
|
logger.F("gatewayID", gatewayID),
|
||||||
|
logger.E(errors.WithStack(err)),
|
||||||
|
)
|
||||||
|
|
||||||
|
delete(c.proxies, gatewayID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewController() *Controller {
|
||||||
|
return &Controller{
|
||||||
|
proxies: make(map[gateway.ID]*ReverseProxy),
|
||||||
|
currentSpecRevision: -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ agent.Controller = &Controller{}
|
@ -1,22 +1,28 @@
|
|||||||
package proxy
|
package gateway
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/proxy"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ReverseProxy struct {
|
type ReverseProxy struct {
|
||||||
addr string
|
addr string
|
||||||
|
target string
|
||||||
server *http.Server
|
server *http.Server
|
||||||
mutex sync.RWMutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ReverseProxy) Start(ctx context.Context, addr string, funcs ...proxy.OptionFunc) error {
|
func (p *ReverseProxy) Start(ctx context.Context, addr, target string) error {
|
||||||
|
alreadyRunning := p.server != nil && target == p.target && addr == p.target
|
||||||
|
|
||||||
|
if alreadyRunning {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if p.server != nil {
|
if p.server != nil {
|
||||||
if err := p.Stop(); err != nil {
|
if err := p.Stop(); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
@ -27,40 +33,33 @@ func (p *ReverseProxy) Start(ctx context.Context, addr string, funcs ...proxy.Op
|
|||||||
Addr: addr,
|
Addr: addr,
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy := proxy.New(funcs...)
|
url, err := url.Parse(target)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy := httputil.NewSingleHostReverseProxy(url)
|
||||||
|
|
||||||
server.Handler = proxy
|
server.Handler = proxy
|
||||||
|
|
||||||
p.mutex.Lock()
|
|
||||||
p.server = server
|
p.server = server
|
||||||
p.addr = addr
|
p.addr = addr
|
||||||
p.mutex.Unlock()
|
p.target = target
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
|
||||||
if err := p.Stop(); err != nil {
|
|
||||||
logger.Error(ctx, "error while stopping gateway", logger.E(errors.WithStack(err)))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
logger.Error(ctx, "error while listening", logger.E(errors.WithStack(err)))
|
logger.Error(ctx, "error while listening", logger.E(errors.WithStack(err)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := p.Stop(); err != nil {
|
||||||
|
logger.Error(ctx, "error while stopping gateway", logger.E(errors.WithStack(err)))
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ReverseProxy) Running() bool {
|
|
||||||
p.mutex.RLock()
|
|
||||||
defer p.mutex.RUnlock()
|
|
||||||
|
|
||||||
return p.server != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ReverseProxy) Stop() error {
|
func (p *ReverseProxy) Stop() error {
|
||||||
p.mutex.Lock()
|
|
||||||
defer p.mutex.Unlock()
|
|
||||||
|
|
||||||
if p.server == nil {
|
if p.server == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
@ -1,31 +0,0 @@
|
|||||||
package openwrt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func hash(path string) (string, error) {
|
|
||||||
file, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return "", errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hasher := sha256.New()
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := file.Close(); err != nil {
|
|
||||||
panic(errors.WithStack(err))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if _, err := io.Copy(hasher, file); err != nil {
|
|
||||||
return "", errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return hex.EncodeToString(hasher.Sum(nil)), nil
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
||||||
"$id": "https://sysupgrade.openwrt.emissary.cadoles.com/spec.json",
|
|
||||||
"title": "SysUpgradeSpec",
|
|
||||||
"description": "Emissary 'SysUpgrade' specification",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"url": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"sha256sum": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"version": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["url", "sha256sum", "version"],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "sysupgrade.openwrt.emissary.cadoles.com",
|
|
||||||
"data": {
|
|
||||||
"url": "http://example.com/firmware.img",
|
|
||||||
"sha256sum": "58019192dacdae17755707719707db007e26dac856102280583fbd18427dd352",
|
|
||||||
"version": "0.0.0"
|
|
||||||
},
|
|
||||||
"revision": 0
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
package sysupgrade
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"io/ioutil"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type validatorTestCase struct {
|
|
||||||
Name string
|
|
||||||
Source string
|
|
||||||
ShouldFail bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var validatorTestCases = []validatorTestCase{
|
|
||||||
{
|
|
||||||
Name: "SpecOK",
|
|
||||||
Source: "testdata/spec-ok.json",
|
|
||||||
ShouldFail: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValidator(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
validator := spec.NewValidator()
|
|
||||||
if err := validator.Register(Name, schema); err != nil {
|
|
||||||
t.Fatalf("+%v", errors.WithStack(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range validatorTestCases {
|
|
||||||
func(tc validatorTestCase) {
|
|
||||||
t.Run(tc.Name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
rawSpec, err := ioutil.ReadFile(tc.Source)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("+%v", errors.WithStack(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
var spec spec.RawSpec
|
|
||||||
|
|
||||||
if err := json.Unmarshal(rawSpec, &spec); err != nil {
|
|
||||||
t.Fatalf("+%v", errors.WithStack(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
err = validator.Validate(ctx, &spec)
|
|
||||||
|
|
||||||
if !tc.ShouldFail && err != nil {
|
|
||||||
t.Errorf("+%v", errors.WithStack(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if tc.ShouldFail && err == nil {
|
|
||||||
t.Error("validation should have failed")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}(tc)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,177 +0,0 @@
|
|||||||
package openwrt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/openwrt/spec/sysupgrade"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SysUpgradeController struct {
|
|
||||||
client *http.Client
|
|
||||||
command string
|
|
||||||
args []string
|
|
||||||
firmwareVersion FirmwareVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name implements agent.Controller
|
|
||||||
func (*SysUpgradeController) Name() string {
|
|
||||||
return "sysupgrade-controller"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reconcile implements agent.Controller
|
|
||||||
func (c *SysUpgradeController) Reconcile(ctx context.Context, state *agent.State) error {
|
|
||||||
sysSpec := sysupgrade.NewSpec()
|
|
||||||
|
|
||||||
if err := state.GetSpec(sysupgrade.Name, sysSpec); err != nil {
|
|
||||||
if errors.Is(err, agent.ErrSpecNotFound) {
|
|
||||||
logger.Info(ctx, "could not find sysupgrade spec, doing nothing")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
firmwareVersion, err := c.firmwareVersion.FirmwareVersion(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = logger.With(ctx,
|
|
||||||
logger.F("currentFirmwareVersion", firmwareVersion),
|
|
||||||
logger.F("newFirmwareVersion", sysSpec.Version),
|
|
||||||
)
|
|
||||||
|
|
||||||
if firmwareVersion == sysSpec.Version {
|
|
||||||
logger.Info(ctx, "firmware version did not change, doing nothing")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadDir, err := os.MkdirTemp(os.TempDir(), "emissary_sysupgrade_*")
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := os.RemoveAll(downloadDir); err != nil {
|
|
||||||
logger.Error(
|
|
||||||
ctx, "could not remove download direction",
|
|
||||||
logger.E(errors.WithStack(err)),
|
|
||||||
logger.F("downloadDir", downloadDir),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
firmwareFile := filepath.Join(downloadDir, "firmware.bin")
|
|
||||||
|
|
||||||
logger.Info(
|
|
||||||
ctx, "downloading firmware",
|
|
||||||
logger.F("url", sysSpec.URL), logger.F("sha256sum", sysSpec.SHA256Sum),
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := c.downloadFile(ctx, sysSpec.URL, sysSpec.SHA256Sum, firmwareFile); err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Info(ctx, "upgrading firmware")
|
|
||||||
|
|
||||||
if err := c.upgradeFirmware(ctx, firmwareFile); err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *SysUpgradeController) downloadFile(ctx context.Context, url string, sha256sum string, dest string) error {
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := c.client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := res.Body.Close(); err != nil && !errors.Is(err, os.ErrClosed) {
|
|
||||||
panic(errors.WithStack(err))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
tmp, err := os.CreateTemp(filepath.Dir(dest), "download_")
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := os.Remove(tmp.Name()); err != nil && !os.IsNotExist(err) {
|
|
||||||
panic(errors.WithStack(err))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if _, err := io.Copy(tmp, res.Body); err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpFileHash, err := hash(tmp.Name())
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tmpFileHash != sha256sum {
|
|
||||||
return errors.Errorf("sha256 sum mismatch: expected '%s', got '%s'", sha256sum, tmpFileHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.Rename(tmp.Name(), dest); err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *SysUpgradeController) upgradeFirmware(ctx context.Context, firmwareFile string) error {
|
|
||||||
templatizedArgs := make([]string, len(c.args))
|
|
||||||
for i, a := range c.args {
|
|
||||||
templatizedArgs[i] = strings.Replace(a, FirmwareFileTemplate, firmwareFile, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
command := exec.CommandContext(ctx, c.command, templatizedArgs...)
|
|
||||||
|
|
||||||
command.Stdout = os.Stdout
|
|
||||||
command.Stderr = os.Stderr
|
|
||||||
|
|
||||||
logger.Debug(ctx, "executing command", logger.F("command", c.command), logger.F("args", templatizedArgs))
|
|
||||||
|
|
||||||
if err := command.Run(); err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSysUpgradeController(funcs ...SysUpgradeOptionFunc) *SysUpgradeController {
|
|
||||||
opts := defaultSysUpgradeOptions()
|
|
||||||
for _, fn := range funcs {
|
|
||||||
fn(opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &SysUpgradeController{
|
|
||||||
command: opts.Command,
|
|
||||||
args: opts.Args,
|
|
||||||
client: opts.Client,
|
|
||||||
firmwareVersion: opts.FirmwareVersion,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ agent.Controller = &SysUpgradeController{}
|
|
@ -1,46 +0,0 @@
|
|||||||
package openwrt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ShellFirmwareVersion struct {
|
|
||||||
command string
|
|
||||||
args []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// FirmwareVersion implements FirmwareVersion
|
|
||||||
func (fv *ShellFirmwareVersion) FirmwareVersion(ctx context.Context) (string, error) {
|
|
||||||
command := exec.CommandContext(ctx, fv.command, fv.args...)
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
|
|
||||||
command.Stdout = &buf
|
|
||||||
command.Stderr = os.Stderr
|
|
||||||
|
|
||||||
logger.Debug(ctx, "executing command", logger.F("command", fv.command), logger.F("args", fv.args))
|
|
||||||
|
|
||||||
if err := command.Run(); err != nil {
|
|
||||||
return "", errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
version := strings.TrimSpace(buf.String())
|
|
||||||
|
|
||||||
return version, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewShellFirmwareVersion(command string, args ...string) *ShellFirmwareVersion {
|
|
||||||
return &ShellFirmwareVersion{
|
|
||||||
command: command,
|
|
||||||
args: args,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ FirmwareVersion = &ShellFirmwareVersion{}
|
|
@ -1,58 +0,0 @@
|
|||||||
package openwrt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const FirmwareFileTemplate = "%FIRMWARE%"
|
|
||||||
|
|
||||||
type FirmwareVersion interface {
|
|
||||||
FirmwareVersion(context.Context) (string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type SysUpgradeOptions struct {
|
|
||||||
Command string
|
|
||||||
Args []string
|
|
||||||
FirmwareVersion FirmwareVersion
|
|
||||||
Client *http.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultSysUpgradeOptions() *SysUpgradeOptions {
|
|
||||||
return &SysUpgradeOptions{
|
|
||||||
Command: `echo`,
|
|
||||||
Args: []string{`[DUMMY UPGRADE]`, FirmwareFileTemplate},
|
|
||||||
Client: &http.Client{
|
|
||||||
Timeout: 30 * time.Second,
|
|
||||||
},
|
|
||||||
FirmwareVersion: NewShellFirmwareVersion(`echo`, "0.0.0-dummy"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type SysUpgradeOptionFunc func(*SysUpgradeOptions)
|
|
||||||
|
|
||||||
func WithSysUpgradeCommand(command string, args ...string) SysUpgradeOptionFunc {
|
|
||||||
return func(opts *SysUpgradeOptions) {
|
|
||||||
opts.Command = command
|
|
||||||
opts.Args = args
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithSysUpgradeFirmwareVersion(firmwareVersion FirmwareVersion) SysUpgradeOptionFunc {
|
|
||||||
return func(opts *SysUpgradeOptions) {
|
|
||||||
opts.FirmwareVersion = firmwareVersion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithSysUpgradeShellFirmwareVersion(command string, args ...string) SysUpgradeOptionFunc {
|
|
||||||
return func(opts *SysUpgradeOptions) {
|
|
||||||
opts.FirmwareVersion = NewShellFirmwareVersion(command, args...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithSysUpgradeClient(client *http.Client) SysUpgradeOptionFunc {
|
|
||||||
return func(opts *SysUpgradeOptions) {
|
|
||||||
opts.Client = client
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,180 +0,0 @@
|
|||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/proxy"
|
|
||||||
spec "forge.cadoles.com/Cadoles/emissary/internal/spec/proxy"
|
|
||||||
"github.com/mitchellh/hashstructure/v2"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
type proxyEntry struct {
|
|
||||||
SpecHash uint64
|
|
||||||
Proxy *ReverseProxy
|
|
||||||
}
|
|
||||||
|
|
||||||
type Controller struct {
|
|
||||||
proxies map[spec.ID]*proxyEntry
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name implements node.Controller.
|
|
||||||
func (c *Controller) Name() string {
|
|
||||||
return "proxy-controller"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reconcile implements node.Controller.
|
|
||||||
func (c *Controller) Reconcile(ctx context.Context, state *agent.State) error {
|
|
||||||
proxySpec := spec.NewSpec()
|
|
||||||
|
|
||||||
if err := state.GetSpec(spec.NameProxy, proxySpec); err != nil {
|
|
||||||
if errors.Is(err, agent.ErrSpecNotFound) {
|
|
||||||
logger.Info(ctx, "could not find proxy spec")
|
|
||||||
|
|
||||||
c.stopAllProxies(ctx)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Info(ctx, "retrieved spec", logger.F("spec", proxySpec.SpecName()), logger.F("revision", proxySpec.SpecRevision()))
|
|
||||||
|
|
||||||
c.updateProxies(ctx, proxySpec)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Controller) stopAllProxies(ctx context.Context) {
|
|
||||||
if len(c.proxies) > 0 {
|
|
||||||
logger.Info(ctx, "stopping all proxies")
|
|
||||||
}
|
|
||||||
|
|
||||||
for proxyID, entry := range c.proxies {
|
|
||||||
logger.Info(ctx, "stopping proxy", logger.F("proxyID", proxyID))
|
|
||||||
|
|
||||||
if err := entry.Proxy.Stop(); err != nil {
|
|
||||||
logger.Error(
|
|
||||||
ctx, "error while stopping proxy",
|
|
||||||
logger.F("proxyID", proxyID),
|
|
||||||
logger.E(errors.WithStack(err)),
|
|
||||||
)
|
|
||||||
|
|
||||||
delete(c.proxies, proxyID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Controller) updateProxies(ctx context.Context, spec *spec.Spec) {
|
|
||||||
// Stop and remove obsolete proxys
|
|
||||||
for proxyID, entry := range c.proxies {
|
|
||||||
if _, exists := spec.Proxies[proxyID]; exists {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Info(ctx, "stopping proxy", logger.F("proxyID", proxyID))
|
|
||||||
|
|
||||||
if err := entry.Proxy.Stop(); err != nil {
|
|
||||||
logger.Error(
|
|
||||||
ctx, "error while stopping proxy",
|
|
||||||
logger.F("proxyID", proxyID),
|
|
||||||
logger.E(errors.WithStack(err)),
|
|
||||||
)
|
|
||||||
|
|
||||||
delete(c.proxies, proxyID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// (Re)start proxys
|
|
||||||
for proxyID, proxySpec := range spec.Proxies {
|
|
||||||
proxyCtx := logger.With(ctx, logger.F("proxyID", proxyID))
|
|
||||||
|
|
||||||
if err := c.updateProxy(ctx, proxyID, proxySpec); err != nil {
|
|
||||||
logger.Error(proxyCtx, "could not update proxy", logger.E(errors.WithStack(err)))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Controller) updateProxy(ctx context.Context, proxyID spec.ID, proxySpec spec.ProxyEntry) (err error) {
|
|
||||||
newProxySpecHash, err := hashstructure.Hash(proxySpec, hashstructure.FormatV2, nil)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var entry *proxyEntry
|
|
||||||
|
|
||||||
entry, exists := c.proxies[proxyID]
|
|
||||||
if !exists {
|
|
||||||
logger.Info(ctx, "proxy currently not running")
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry == nil {
|
|
||||||
entry = &proxyEntry{
|
|
||||||
Proxy: NewReverseProxy(),
|
|
||||||
SpecHash: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
c.proxies[proxyID] = entry
|
|
||||||
}
|
|
||||||
|
|
||||||
specChanged := newProxySpecHash != entry.SpecHash
|
|
||||||
|
|
||||||
if entry.Proxy.Running() && !specChanged {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if specChanged && entry.SpecHash != 0 {
|
|
||||||
logger.Info(
|
|
||||||
ctx, "restarting proxy",
|
|
||||||
logger.F("address", proxySpec.Address),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
logger.Info(
|
|
||||||
ctx, "starting proxy",
|
|
||||||
logger.F("address", proxySpec.Address),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
options := make([]proxy.OptionFunc, 0)
|
|
||||||
allowedHosts := make([]string, len(proxySpec.Mappings))
|
|
||||||
mappings := make(map[string]*url.URL, len(proxySpec.Mappings))
|
|
||||||
|
|
||||||
for _, m := range proxySpec.Mappings {
|
|
||||||
target, err := url.Parse(m.Target)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
mappings[m.HostPattern] = target
|
|
||||||
allowedHosts = append(allowedHosts, m.HostPattern)
|
|
||||||
}
|
|
||||||
|
|
||||||
options = append(
|
|
||||||
options,
|
|
||||||
proxy.WithAllowedHosts(allowedHosts...),
|
|
||||||
proxy.WithRewriteHosts(mappings),
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := entry.Proxy.Start(ctx, proxySpec.Address, options...); err != nil {
|
|
||||||
delete(c.proxies, proxyID)
|
|
||||||
|
|
||||||
return errors.Wrap(err, "could not start app")
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.SpecHash = newProxySpecHash
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewController() *Controller {
|
|
||||||
return &Controller{
|
|
||||||
proxies: make(map[spec.ID]*proxyEntry),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ agent.Controller = &Controller{}
|
|
@ -21,15 +21,22 @@ func (c *Controller) Name() string {
|
|||||||
func (c *Controller) Reconcile(ctx context.Context, state *agent.State) error {
|
func (c *Controller) Reconcile(ctx context.Context, state *agent.State) error {
|
||||||
cl := agent.Client(ctx)
|
cl := agent.Client(ctx)
|
||||||
|
|
||||||
agent, err := cl.GetAgent(
|
agents, _, err := cl.QueryAgents(
|
||||||
ctx,
|
ctx,
|
||||||
state.AgentID(),
|
client.WithQueryAgentsLimit(1),
|
||||||
|
client.WithQueryAgentsID(state.AgentID()),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.reconcileAgent(ctx, cl, state, agent); err != nil {
|
if len(agents) == 0 {
|
||||||
|
logger.Error(ctx, "could not find remote matching agent")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.reconcileAgent(ctx, cl, state, agents[0]); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ func GenerateToken(key jwk.Key, thumbprint string) (string, error) {
|
|||||||
return "", errors.WithStack(err)
|
return "", errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now().UTC()
|
now := time.Now()
|
||||||
|
|
||||||
if err := token.Set(jwt.NotBeforeKey, now); err != nil {
|
if err := token.Set(jwt.NotBeforeKey, now); err != nil {
|
||||||
return "", errors.WithStack(err)
|
return "", errors.WithStack(err)
|
||||||
|
@ -13,7 +13,7 @@ type User struct {
|
|||||||
|
|
||||||
// Subject implements auth.User
|
// Subject implements auth.User
|
||||||
func (u *User) Subject() string {
|
func (u *User) Subject() string {
|
||||||
return fmt.Sprintf("agent-%d", u.agent.ID)
|
return fmt.Sprintf("agent#%d", u.agent.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) Agent() *datastore.Agent {
|
func (u *User) Agent() *datastore.Agent {
|
||||||
|
@ -20,8 +20,8 @@ const (
|
|||||||
contextKeyUser contextKey = "user"
|
contextKeyUser contextKey = "user"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CtxUser(ctx context.Context) (User, error) {
|
func CtxUser(ctx context.Context) (*User, error) {
|
||||||
user, ok := ctx.Value(contextKeyUser).(User)
|
user, ok := ctx.Value(contextKeyUser).(*User)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.Errorf("unexpected user type: expected '%T', got '%T'", new(User), ctx.Value(contextKeyUser))
|
return nil, errors.Errorf("unexpected user type: expected '%T', got '%T'", new(User), ctx.Value(contextKeyUser))
|
||||||
}
|
}
|
||||||
@ -29,7 +29,10 @@ func CtxUser(ctx context.Context) (User, error) {
|
|||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrUnauthenticated = errors.New("unauthenticated")
|
var (
|
||||||
|
ErrUnauthenticated = errors.New("unauthenticated")
|
||||||
|
ErrForbidden = errors.New("forbidden")
|
||||||
|
)
|
||||||
|
|
||||||
type User interface {
|
type User interface {
|
||||||
Subject() string
|
Subject() string
|
||||||
@ -52,7 +55,7 @@ func Middleware(authenticators ...Authenticator) func(http.Handler) http.Handler
|
|||||||
for _, auth := range authenticators {
|
for _, auth := range authenticators {
|
||||||
user, err = auth.Authenticate(ctx, r)
|
user, err = auth.Authenticate(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Debug(ctx, "could not authenticate request", logger.E(errors.WithStack(err)))
|
logger.Warn(ctx, "could not authenticate request", logger.E(errors.WithStack(err)))
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -68,7 +71,6 @@ func Middleware(authenticators ...Authenticator) func(http.Handler) http.Handler
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = logger.With(ctx, logger.F("user", user.Subject()))
|
|
||||||
ctx = context.WithValue(ctx, contextKeyUser, user)
|
ctx = context.WithValue(ctx, contextKeyUser, user)
|
||||||
|
|
||||||
h.ServeHTTP(w, r.WithContext(ctx))
|
h.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package thirdparty
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
@ -1,4 +1,4 @@
|
|||||||
package thirdparty
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -27,22 +27,14 @@ func parseToken(ctx context.Context, keys jwk.Set, issuer string, rawToken strin
|
|||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateToken(ctx context.Context, key jwk.Key, issuer, subject string, role Role) (string, error) {
|
func GenerateToken(ctx context.Context, key jwk.Key, role Role) (string, error) {
|
||||||
token := jwt.New()
|
token := jwt.New()
|
||||||
|
|
||||||
if err := token.Set(jwt.SubjectKey, subject); err != nil {
|
|
||||||
return "", errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := token.Set(jwt.IssuerKey, issuer); err != nil {
|
|
||||||
return "", errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := token.Set(keyRole, role); err != nil {
|
if err := token.Set(keyRole, role); err != nil {
|
||||||
return "", errors.WithStack(err)
|
return "", errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now().UTC()
|
now := time.Now()
|
||||||
|
|
||||||
if err := token.Set(jwt.NotBeforeKey, now); err != nil {
|
if err := token.Set(jwt.NotBeforeKey, now); err != nil {
|
||||||
return "", errors.WithStack(err)
|
return "", errors.WithStack(err)
|
@ -1,4 +1,4 @@
|
|||||||
package thirdparty
|
package user
|
||||||
|
|
||||||
import "forge.cadoles.com/Cadoles/emissary/internal/auth"
|
import "forge.cadoles.com/Cadoles/emissary/internal/auth"
|
||||||
|
|
@ -1,27 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Client) DeleteAgent(ctx context.Context, agentID datastore.AgentID, funcs ...OptionFunc) (datastore.AgentID, error) {
|
|
||||||
response := withResponse[struct {
|
|
||||||
AgentID int64 `json:"agentId"`
|
|
||||||
}]()
|
|
||||||
|
|
||||||
path := fmt.Sprintf("/api/v1/agents/%d", agentID)
|
|
||||||
|
|
||||||
if err := c.apiDelete(ctx, path, nil, &response, funcs...); err != nil {
|
|
||||||
return 0, errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.Error != nil {
|
|
||||||
return 0, errors.WithStack(response.Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return datastore.AgentID(response.Data.AgentID), nil
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Client) DeleteAgentSpec(ctx context.Context, agentID datastore.AgentID, name spec.Name, funcs ...OptionFunc) (spec.Name, error) {
|
|
||||||
payload := struct {
|
|
||||||
Name spec.Name `json:"name"`
|
|
||||||
}{
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
response := withResponse[struct {
|
|
||||||
Name spec.Name `json:"name"`
|
|
||||||
}]()
|
|
||||||
|
|
||||||
path := fmt.Sprintf("/api/v1/agents/%d/specs", agentID)
|
|
||||||
|
|
||||||
if err := c.apiDelete(ctx, path, payload, &response, funcs...); err != nil {
|
|
||||||
return "", errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.Error != nil {
|
|
||||||
return "", errors.WithStack(response.Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Data.Name, nil
|
|
||||||
}
|
|
@ -10,7 +10,6 @@ import (
|
|||||||
|
|
||||||
type UpdateAgentOptions struct {
|
type UpdateAgentOptions struct {
|
||||||
Status *int
|
Status *int
|
||||||
Label *string
|
|
||||||
Options []OptionFunc
|
Options []OptionFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,12 +21,6 @@ func WithAgentStatus(status int) UpdateAgentOptionFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithAgentLabel(label string) UpdateAgentOptionFunc {
|
|
||||||
return func(opts *UpdateAgentOptions) {
|
|
||||||
opts.Label = &label
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithUpdateAgentsOptions(funcs ...OptionFunc) UpdateAgentOptionFunc {
|
func WithUpdateAgentsOptions(funcs ...OptionFunc) UpdateAgentOptionFunc {
|
||||||
return func(opts *UpdateAgentOptions) {
|
return func(opts *UpdateAgentOptions) {
|
||||||
opts.Options = funcs
|
opts.Options = funcs
|
||||||
@ -46,10 +39,6 @@ func (c *Client) UpdateAgent(ctx context.Context, agentID datastore.AgentID, fun
|
|||||||
payload["status"] = *opts.Status
|
payload["status"] = *opts.Status
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Label != nil {
|
|
||||||
payload["label"] = *opts.Label
|
|
||||||
}
|
|
||||||
|
|
||||||
response := withResponse[struct {
|
response := withResponse[struct {
|
||||||
Agent *datastore.Agent `json:"agent"`
|
Agent *datastore.Agent `json:"agent"`
|
||||||
}]()
|
}]()
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/common"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/common"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/openwrt/uci"
|
"forge.cadoles.com/Cadoles/emissary/internal/openwrt/uci"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
_ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ func Root() *cli.Command {
|
|||||||
openwrt.Root(),
|
openwrt.Root(),
|
||||||
config.Root(),
|
config.Root(),
|
||||||
RunCommand(),
|
RunCommand(),
|
||||||
ShowThumbprintCommand(),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,9 @@ import (
|
|||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent"
|
"forge.cadoles.com/Cadoles/emissary/internal/agent"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/app"
|
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/app"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/gateway"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/openwrt"
|
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/openwrt"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/persistence"
|
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/persistence"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/proxy"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/spec"
|
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/spec"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent/metadata"
|
"forge.cadoles.com/Cadoles/emissary/internal/agent/metadata"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/agent/metadata/collector/buildinfo"
|
"forge.cadoles.com/Cadoles/emissary/internal/agent/metadata/collector/buildinfo"
|
||||||
@ -17,6 +17,7 @@ import (
|
|||||||
"forge.cadoles.com/Cadoles/emissary/internal/jwk"
|
"forge.cadoles.com/Cadoles/emissary/internal/jwk"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/machineid"
|
"forge.cadoles.com/Cadoles/emissary/internal/machineid"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
_ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
)
|
)
|
||||||
@ -49,6 +50,10 @@ func RunCommand() *cli.Command {
|
|||||||
controllers = append(controllers, spec.NewController())
|
controllers = append(controllers, spec.NewController())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctrlConf.Gateway.Enabled {
|
||||||
|
controllers = append(controllers, gateway.NewController())
|
||||||
|
}
|
||||||
|
|
||||||
if ctrlConf.UCI.Enabled {
|
if ctrlConf.UCI.Enabled {
|
||||||
controllers = append(controllers, openwrt.NewUCIController(
|
controllers = append(controllers, openwrt.NewUCIController(
|
||||||
string(ctrlConf.UCI.BinPath),
|
string(ctrlConf.UCI.BinPath),
|
||||||
@ -62,33 +67,6 @@ func RunCommand() *cli.Command {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctrlConf.Proxy.Enabled {
|
|
||||||
controllers = append(controllers, proxy.NewController())
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctrlConf.SysUpgrade.Enabled {
|
|
||||||
sysUpgradeArgs := make([]string, 0)
|
|
||||||
if len(ctrlConf.SysUpgrade.SysUpgradeCommand) > 1 {
|
|
||||||
sysUpgradeArgs = ctrlConf.SysUpgrade.SysUpgradeCommand[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
firmwareVersionArgs := make([]string, 0)
|
|
||||||
if len(ctrlConf.SysUpgrade.FirmwareVersionCommand) > 1 {
|
|
||||||
firmwareVersionArgs = ctrlConf.SysUpgrade.FirmwareVersionCommand[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
controllers = append(controllers, openwrt.NewSysUpgradeController(
|
|
||||||
openwrt.WithSysUpgradeCommand(
|
|
||||||
ctrlConf.SysUpgrade.SysUpgradeCommand[0],
|
|
||||||
sysUpgradeArgs...,
|
|
||||||
),
|
|
||||||
openwrt.WithSysUpgradeShellFirmwareVersion(
|
|
||||||
ctrlConf.SysUpgrade.FirmwareVersionCommand[0],
|
|
||||||
firmwareVersionArgs...,
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := jwk.LoadOrGenerate(string(conf.Agent.PrivateKeyPath), jwk.DefaultKeySize)
|
key, err := jwk.LoadOrGenerate(string(conf.Agent.PrivateKeyPath), jwk.DefaultKeySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
package agent
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/common"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/machineid"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ShowThumbprintCommand() *cli.Command {
|
|
||||||
flags := common.Flags()
|
|
||||||
|
|
||||||
return &cli.Command{
|
|
||||||
Name: "show-thumbprint",
|
|
||||||
Usage: "Show the current agent thumbprint",
|
|
||||||
Flags: flags,
|
|
||||||
Action: func(ctx *cli.Context) error {
|
|
||||||
thumbprint, err := machineid.Get()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(thumbprint)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
package agent
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
|
||||||
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
|
||||||
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func DeleteCommand() *cli.Command {
|
|
||||||
return &cli.Command{
|
|
||||||
Name: "delete",
|
|
||||||
Usage: "Delete agent",
|
|
||||||
Flags: agentFlag.WithAgentFlags(),
|
|
||||||
Action: func(ctx *cli.Context) error {
|
|
||||||
baseFlags := clientFlag.GetBaseFlags(ctx)
|
|
||||||
|
|
||||||
token, err := clientFlag.GetToken(baseFlags)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(apierr.Wrap(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
agentID, err := agentFlag.AssertAgentID(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
client := client.New(baseFlags.ServerURL, client.WithToken(token))
|
|
||||||
|
|
||||||
agentID, err = client.DeleteAgent(ctx.Context, agentID)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(apierr.Wrap(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
hints := format.Hints{
|
|
||||||
OutputMode: baseFlags.OutputMode,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := format.Write(baseFlags.Format, os.Stdout, hints, struct {
|
|
||||||
ID datastore.AgentID `json:"id"`
|
|
||||||
}{
|
|
||||||
ID: agentID,
|
|
||||||
}); err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
package agent
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
|
||||||
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func QueryCommand() *cli.Command {
|
|
||||||
return &cli.Command{
|
|
||||||
Name: "query",
|
|
||||||
Usage: "Query agents",
|
|
||||||
Flags: clientFlag.ComposeFlags(
|
|
||||||
&cli.StringSliceFlag{
|
|
||||||
Name: "thumbprints",
|
|
||||||
Usage: "use `THUMBPRINTS` as query filter",
|
|
||||||
Value: nil,
|
|
||||||
},
|
|
||||||
&cli.Int64SliceFlag{
|
|
||||||
Name: "statuses",
|
|
||||||
Usage: "use `STATUSES` as query filter",
|
|
||||||
},
|
|
||||||
&cli.Int64SliceFlag{
|
|
||||||
Name: "ids",
|
|
||||||
Usage: "use `IDS` as query filter",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Action: func(ctx *cli.Context) error {
|
|
||||||
baseFlags := clientFlag.GetBaseFlags(ctx)
|
|
||||||
|
|
||||||
token, err := clientFlag.GetToken(baseFlags)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(apierr.Wrap(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
options := make([]client.QueryAgentsOptionFunc, 0)
|
|
||||||
|
|
||||||
thumbprints := ctx.StringSlice("thumbprints")
|
|
||||||
if thumbprints != nil {
|
|
||||||
options = append(options, client.WithQueryAgentsThumbprints(thumbprints...))
|
|
||||||
}
|
|
||||||
|
|
||||||
rawIDs := ctx.Int64Slice("ids")
|
|
||||||
if rawIDs != nil {
|
|
||||||
agentIDs := func(ids []int64) []datastore.AgentID {
|
|
||||||
agentIDs := make([]datastore.AgentID, len(ids))
|
|
||||||
for i, id := range ids {
|
|
||||||
agentIDs[i] = datastore.AgentID(id)
|
|
||||||
}
|
|
||||||
return agentIDs
|
|
||||||
}(rawIDs)
|
|
||||||
options = append(options, client.WithQueryAgentsID(agentIDs...))
|
|
||||||
}
|
|
||||||
|
|
||||||
rawStatuses := ctx.Int64Slice("statuses")
|
|
||||||
if rawStatuses != nil {
|
|
||||||
statuses := func(rawStatuses []int64) []datastore.AgentStatus {
|
|
||||||
statuses := make([]datastore.AgentStatus, len(rawStatuses))
|
|
||||||
for i, status := range rawStatuses {
|
|
||||||
statuses[i] = datastore.AgentStatus(status)
|
|
||||||
}
|
|
||||||
return statuses
|
|
||||||
}(rawStatuses)
|
|
||||||
options = append(options, client.WithQueryAgentsStatus(statuses...))
|
|
||||||
}
|
|
||||||
|
|
||||||
client := client.New(baseFlags.ServerURL, client.WithToken(token))
|
|
||||||
|
|
||||||
agents, _, err := client.QueryAgents(ctx.Context, options...)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(apierr.Wrap(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
hints := agentHints(baseFlags.OutputMode)
|
|
||||||
|
|
||||||
if err := format.Write(baseFlags.Format, os.Stdout, hints, clientFlag.AsAnySlice(agents)...); err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
package spec
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
|
||||||
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
|
||||||
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func DeleteCommand() *cli.Command {
|
|
||||||
return &cli.Command{
|
|
||||||
Name: "delete",
|
|
||||||
Usage: "Delete spec",
|
|
||||||
|
|
||||||
Flags: agentFlag.WithAgentFlags(
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "spec-name",
|
|
||||||
Usage: "use `NAME` as specification's name",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Action: func(ctx *cli.Context) error {
|
|
||||||
baseFlags := clientFlag.GetBaseFlags(ctx)
|
|
||||||
|
|
||||||
token, err := clientFlag.GetToken(baseFlags)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(apierr.Wrap(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
agentID, err := agentFlag.AssertAgentID(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
specName, err := assertSpecName(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
client := client.New(baseFlags.ServerURL, client.WithToken(token))
|
|
||||||
|
|
||||||
specName, err = client.DeleteAgentSpec(ctx.Context, agentID, specName)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(apierr.Wrap(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
hints := format.Hints{
|
|
||||||
OutputMode: baseFlags.OutputMode,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := format.Write(baseFlags.Format, os.Stdout, hints, struct {
|
|
||||||
Name spec.Name `json:"name"`
|
|
||||||
}{
|
|
||||||
Name: specName,
|
|
||||||
}); err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/agent"
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Root() *cli.Command {
|
|
||||||
return &cli.Command{
|
|
||||||
Name: "api",
|
|
||||||
Usage: "API related commands",
|
|
||||||
Subcommands: []*cli.Command{
|
|
||||||
agent.Root(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,8 +4,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/client/apierr"
|
||||||
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/client/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
@ -18,13 +18,7 @@ func CountCommand() *cli.Command {
|
|||||||
Flags: clientFlag.ComposeFlags(),
|
Flags: clientFlag.ComposeFlags(),
|
||||||
Action: func(ctx *cli.Context) error {
|
Action: func(ctx *cli.Context) error {
|
||||||
baseFlags := clientFlag.GetBaseFlags(ctx)
|
baseFlags := clientFlag.GetBaseFlags(ctx)
|
||||||
|
client := client.New(baseFlags.ServerURL)
|
||||||
token, err := clientFlag.GetToken(baseFlags)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(apierr.Wrap(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
client := client.New(baseFlags.ServerURL, client.WithToken(token))
|
|
||||||
|
|
||||||
_, total, err := client.QueryAgents(ctx.Context)
|
_, total, err := client.QueryAgents(ctx.Context)
|
||||||
if err != nil {
|
if err != nil {
|
@ -3,7 +3,7 @@ package flag
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/client/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
@ -4,9 +4,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
||||||
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/client/agent/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/client/apierr"
|
||||||
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/client/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
@ -20,17 +20,12 @@ func GetCommand() *cli.Command {
|
|||||||
Action: func(ctx *cli.Context) error {
|
Action: func(ctx *cli.Context) error {
|
||||||
baseFlags := clientFlag.GetBaseFlags(ctx)
|
baseFlags := clientFlag.GetBaseFlags(ctx)
|
||||||
|
|
||||||
token, err := clientFlag.GetToken(baseFlags)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(apierr.Wrap(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
agentID, err := agentFlag.AssertAgentID(ctx)
|
agentID, err := agentFlag.AssertAgentID(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
client := client.New(baseFlags.ServerURL, client.WithToken(token))
|
client := client.New(baseFlags.ServerURL)
|
||||||
|
|
||||||
agent, err := client.GetAgent(ctx.Context, agentID)
|
agent, err := client.GetAgent(ctx.Context, agentID)
|
||||||
if err != nil {
|
if err != nil {
|
37
internal/command/client/agent/query.go
Normal file
37
internal/command/client/agent/query.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/command/client/apierr"
|
||||||
|
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/client/flag"
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func QueryCommand() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "query",
|
||||||
|
Usage: "Query agents",
|
||||||
|
Flags: clientFlag.ComposeFlags(),
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
baseFlags := clientFlag.GetBaseFlags(ctx)
|
||||||
|
client := client.New(baseFlags.ServerURL)
|
||||||
|
|
||||||
|
agents, _, err := client.QueryAgents(ctx.Context)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(apierr.Wrap(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
hints := agentHints(baseFlags.OutputMode)
|
||||||
|
|
||||||
|
if err := format.Write(baseFlags.Format, os.Stdout, hints, clientFlag.AsAnySlice(agents)...); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package agent
|
package agent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/spec"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/client/agent/spec"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,7 +14,6 @@ func Root() *cli.Command {
|
|||||||
CountCommand(),
|
CountCommand(),
|
||||||
UpdateCommand(),
|
UpdateCommand(),
|
||||||
GetCommand(),
|
GetCommand(),
|
||||||
DeleteCommand(),
|
|
||||||
spec.Root(),
|
spec.Root(),
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -4,9 +4,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
||||||
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/client/agent/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/client/apierr"
|
||||||
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/client/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
@ -24,12 +24,7 @@ func GetCommand() *cli.Command {
|
|||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := clientFlag.GetToken(baseFlags)
|
client := client.New(baseFlags.ServerURL)
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(apierr.Wrap(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
client := client.New(baseFlags.ServerURL, client.WithToken(token))
|
|
||||||
|
|
||||||
specs, err := client.GetAgentSpecs(ctx.Context, agentID)
|
specs, err := client.GetAgentSpecs(ctx.Context, agentID)
|
||||||
if err != nil {
|
if err != nil {
|
@ -11,7 +11,6 @@ func Root() *cli.Command {
|
|||||||
Subcommands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
GetCommand(),
|
GetCommand(),
|
||||||
UpdateCommand(),
|
UpdateCommand(),
|
||||||
DeleteCommand(),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,14 +5,19 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
||||||
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/client/agent/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/client/apierr"
|
||||||
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/client/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
||||||
jsonpatch "github.com/evanphx/json-patch/v5"
|
jsonpatch "github.com/evanphx/json-patch/v5"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
// Import specs
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/app"
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/gateway"
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/uci"
|
||||||
)
|
)
|
||||||
|
|
||||||
func UpdateCommand() *cli.Command {
|
func UpdateCommand() *cli.Command {
|
||||||
@ -22,11 +27,11 @@ func UpdateCommand() *cli.Command {
|
|||||||
Flags: agentFlag.WithAgentFlags(
|
Flags: agentFlag.WithAgentFlags(
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "spec-name",
|
Name: "spec-name",
|
||||||
Usage: "use `NAME` as specification's name",
|
Usage: "use `NAME` as spec name",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "spec-data",
|
Name: "spec-data",
|
||||||
Usage: "use `DATA` as specification's data, '-' to read from STDIN",
|
Usage: "use `DATA` as spec data, '-' to read from STDIN",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "no-patch",
|
Name: "no-patch",
|
||||||
@ -56,12 +61,7 @@ func UpdateCommand() *cli.Command {
|
|||||||
|
|
||||||
noPatch := ctx.Bool("no-patch")
|
noPatch := ctx.Bool("no-patch")
|
||||||
|
|
||||||
token, err := clientFlag.GetToken(baseFlags)
|
client := client.New(baseFlags.ServerURL)
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(apierr.Wrap(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
client := client.New(baseFlags.ServerURL, client.WithToken(token))
|
|
||||||
|
|
||||||
specs, err := client.GetAgentSpecs(ctx.Context, agentID)
|
specs, err := client.GetAgentSpecs(ctx.Context, agentID)
|
||||||
if err != nil {
|
if err != nil {
|
@ -4,9 +4,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
"forge.cadoles.com/Cadoles/emissary/internal/client"
|
||||||
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/agent/flag"
|
agentFlag "forge.cadoles.com/Cadoles/emissary/internal/command/client/agent/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/api/apierr"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/client/apierr"
|
||||||
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/api/flag"
|
clientFlag "forge.cadoles.com/Cadoles/emissary/internal/command/client/flag"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
@ -22,20 +22,10 @@ func UpdateCommand() *cli.Command {
|
|||||||
Usage: "Set `STATUS` to selected agent",
|
Usage: "Set `STATUS` to selected agent",
|
||||||
Value: -1,
|
Value: -1,
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "label",
|
|
||||||
Usage: "Set `LABEL` to selected agent",
|
|
||||||
Value: "",
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
Action: func(ctx *cli.Context) error {
|
Action: func(ctx *cli.Context) error {
|
||||||
baseFlags := clientFlag.GetBaseFlags(ctx)
|
baseFlags := clientFlag.GetBaseFlags(ctx)
|
||||||
|
|
||||||
token, err := clientFlag.GetToken(baseFlags)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(apierr.Wrap(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
agentID, err := agentFlag.AssertAgentID(ctx)
|
agentID, err := agentFlag.AssertAgentID(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
@ -48,12 +38,7 @@ func UpdateCommand() *cli.Command {
|
|||||||
options = append(options, client.WithAgentStatus(status))
|
options = append(options, client.WithAgentStatus(status))
|
||||||
}
|
}
|
||||||
|
|
||||||
label := ctx.String("label")
|
client := client.New(baseFlags.ServerURL)
|
||||||
if label != "" {
|
|
||||||
options = append(options, client.WithAgentLabel(label))
|
|
||||||
}
|
|
||||||
|
|
||||||
client := client.New(baseFlags.ServerURL, client.WithToken(token))
|
|
||||||
|
|
||||||
agent, err := client.UpdateAgent(ctx.Context, agentID, options...)
|
agent, err := client.UpdateAgent(ctx.Context, agentID, options...)
|
||||||
if err != nil {
|
if err != nil {
|
@ -7,7 +7,6 @@ func agentHints(outputMode format.OutputMode) format.Hints {
|
|||||||
OutputMode: outputMode,
|
OutputMode: outputMode,
|
||||||
Props: []format.Prop{
|
Props: []format.Prop{
|
||||||
format.NewProp("ID", "ID"),
|
format.NewProp("ID", "ID"),
|
||||||
format.NewProp("Label", "Label"),
|
|
||||||
format.NewProp("Thumbprint", "Thumbprint"),
|
format.NewProp("Thumbprint", "Thumbprint"),
|
||||||
format.NewProp("Status", "Status"),
|
format.NewProp("Status", "Status"),
|
||||||
format.NewProp("CreatedAt", "CreatedAt"),
|
format.NewProp("CreatedAt", "CreatedAt"),
|
@ -2,13 +2,9 @@ package flag
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
"forge.cadoles.com/Cadoles/emissary/internal/format"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/format/table"
|
"forge.cadoles.com/Cadoles/emissary/internal/format/table"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,17 +28,6 @@ func ComposeFlags(flags ...cli.Flag) []cli.Flag {
|
|||||||
Usage: fmt.Sprintf("use `MODE` as output mode (available: %s)", []format.OutputMode{format.OutputModeCompact, format.OutputModeWide}),
|
Usage: fmt.Sprintf("use `MODE` as output mode (available: %s)", []format.OutputMode{format.OutputModeCompact, format.OutputModeWide}),
|
||||||
Value: string(format.OutputModeCompact),
|
Value: string(format.OutputModeCompact),
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "token",
|
|
||||||
Aliases: []string{"t"},
|
|
||||||
Usage: "use `TOKEN` as authentication token",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "token-file",
|
|
||||||
Usage: "use `TOKEN_FILE` as file containing the authentication token",
|
|
||||||
Value: ".emissary-token",
|
|
||||||
TakesFile: true,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
flags = append(flags, baseFlags...)
|
flags = append(flags, baseFlags...)
|
||||||
@ -54,43 +39,16 @@ type BaseFlags struct {
|
|||||||
ServerURL string
|
ServerURL string
|
||||||
Format format.Format
|
Format format.Format
|
||||||
OutputMode format.OutputMode
|
OutputMode format.OutputMode
|
||||||
Token string
|
|
||||||
TokenFile string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetBaseFlags(ctx *cli.Context) *BaseFlags {
|
func GetBaseFlags(ctx *cli.Context) *BaseFlags {
|
||||||
serverURL := ctx.String("server")
|
serverURL := ctx.String("server")
|
||||||
rawFormat := ctx.String("format")
|
rawFormat := ctx.String("format")
|
||||||
rawOutputMode := ctx.String("output-mode")
|
rawOutputMode := ctx.String("output-mode")
|
||||||
tokenFile := ctx.String("token-file")
|
|
||||||
token := ctx.String("token")
|
|
||||||
|
|
||||||
return &BaseFlags{
|
return &BaseFlags{
|
||||||
ServerURL: serverURL,
|
ServerURL: serverURL,
|
||||||
Format: format.Format(rawFormat),
|
Format: format.Format(rawFormat),
|
||||||
OutputMode: format.OutputMode(rawOutputMode),
|
OutputMode: format.OutputMode(rawOutputMode),
|
||||||
Token: token,
|
|
||||||
TokenFile: tokenFile,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetToken(flags *BaseFlags) (string, error) {
|
|
||||||
if flags.Token != "" {
|
|
||||||
return flags.Token, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if flags.TokenFile == "" {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rawToken, err := ioutil.ReadFile(flags.TokenFile)
|
|
||||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
||||||
return "", errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if rawToken == nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.TrimSpace(string(rawToken)), nil
|
|
||||||
}
|
|
20
internal/command/client/root.go
Normal file
20
internal/command/client/root.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"forge.cadoles.com/Cadoles/emissary/internal/command/client/agent"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
// Output format
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/format/json"
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/format/table"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Root() *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "client",
|
||||||
|
Usage: "Client related commands",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
agent.Root(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ import (
|
|||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/common"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/common"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/config"
|
"forge.cadoles.com/Cadoles/emissary/internal/config"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
_ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
)
|
)
|
||||||
|
@ -9,6 +9,10 @@ import (
|
|||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
// Spec validation
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/gateway"
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/uci"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Main(buildDate, projectVersion, gitRef, defaultConfigPath string, commands ...*cli.Command) {
|
func Main(buildDate, projectVersion, gitRef, defaultConfigPath string, commands ...*cli.Command) {
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
package auth
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/auth/thirdparty"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/common"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/jwk"
|
|
||||||
"github.com/lithammer/shortuuid/v4"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CreateTokenCommand() *cli.Command {
|
|
||||||
return &cli.Command{
|
|
||||||
Name: "create-token",
|
|
||||||
Usage: "Create a new authentication token",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "role",
|
|
||||||
Usage: fmt.Sprintf("associate `ROLE` to the token (available: %v)", []thirdparty.Role{thirdparty.RoleReader, thirdparty.RoleWriter}),
|
|
||||||
Value: string(thirdparty.RoleReader),
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "subject",
|
|
||||||
Usage: "associate `SUBJECT` to the token",
|
|
||||||
Value: fmt.Sprintf("user-%s", shortuuid.New()),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: func(ctx *cli.Context) error {
|
|
||||||
conf, err := common.LoadConfig(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "Could not load configuration")
|
|
||||||
}
|
|
||||||
|
|
||||||
subject := ctx.String("subject")
|
|
||||||
role := ctx.String("role")
|
|
||||||
|
|
||||||
key, err := jwk.LoadOrGenerate(string(conf.Server.PrivateKeyPath), jwk.DefaultKeySize)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
token, err := thirdparty.GenerateToken(ctx.Context, key, string(conf.Server.Issuer), subject, thirdparty.Role(role))
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(token)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,10 +6,8 @@ import (
|
|||||||
|
|
||||||
func Root() *cli.Command {
|
func Root() *cli.Command {
|
||||||
return &cli.Command{
|
return &cli.Command{
|
||||||
Name: "auth",
|
Name: "auth",
|
||||||
Usage: "Authentication related commands",
|
Usage: "Authentication related commands",
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []*cli.Command{},
|
||||||
CreateTokenCommand(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"forge.cadoles.com/Cadoles/emissary/internal/command/common"
|
"forge.cadoles.com/Cadoles/emissary/internal/command/common"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/server"
|
"forge.cadoles.com/Cadoles/emissary/internal/server"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
_ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import "forge.cadoles.com/Cadoles/emissary/internal/agent/controller/openwrt"
|
|
||||||
|
|
||||||
type AgentConfig struct {
|
type AgentConfig struct {
|
||||||
ServerURL InterpolatedString `yaml:"serverUrl"`
|
ServerURL InterpolatedString `yaml:"serverUrl"`
|
||||||
PrivateKeyPath InterpolatedString `yaml:"privateKeyPath"`
|
PrivateKeyPath InterpolatedString `yaml:"privateKeyPath"`
|
||||||
@ -19,10 +17,9 @@ type ShellCollectorConfig struct {
|
|||||||
type ControllersConfig struct {
|
type ControllersConfig struct {
|
||||||
Persistence PersistenceControllerConfig `yaml:"persistence"`
|
Persistence PersistenceControllerConfig `yaml:"persistence"`
|
||||||
Spec SpecControllerConfig `yaml:"spec"`
|
Spec SpecControllerConfig `yaml:"spec"`
|
||||||
Proxy ProxyControllerConfig `yaml:"proxy"`
|
Gateway GatewayControllerConfig `yaml:"gateway"`
|
||||||
UCI UCIControllerConfig `yaml:"uci"`
|
UCI UCIControllerConfig `yaml:"uci"`
|
||||||
App AppControllerConfig `yaml:"app"`
|
App AppControllerConfig `yaml:"app"`
|
||||||
SysUpgrade SysUpgradeControllerConfig `yaml:"sysupgrade"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PersistenceControllerConfig struct {
|
type PersistenceControllerConfig struct {
|
||||||
@ -33,7 +30,7 @@ type PersistenceControllerConfig struct {
|
|||||||
type SpecControllerConfig struct {
|
type SpecControllerConfig struct {
|
||||||
Enabled InterpolatedBool `yaml:"enabled"`
|
Enabled InterpolatedBool `yaml:"enabled"`
|
||||||
}
|
}
|
||||||
type ProxyControllerConfig struct {
|
type GatewayControllerConfig struct {
|
||||||
Enabled InterpolatedBool `yaml:"enabled"`
|
Enabled InterpolatedBool `yaml:"enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,12 +46,6 @@ type AppControllerConfig struct {
|
|||||||
DownloadDir InterpolatedString `yaml:"downloadDir"`
|
DownloadDir InterpolatedString `yaml:"downloadDir"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SysUpgradeControllerConfig struct {
|
|
||||||
Enabled InterpolatedBool `yaml:"enabled"`
|
|
||||||
SysUpgradeCommand InterpolatedStringSlice `yaml:"sysupgradeCommand"`
|
|
||||||
FirmwareVersionCommand InterpolatedStringSlice `yaml:"firmwareVersionCommand"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDefaultAgentConfig() AgentConfig {
|
func NewDefaultAgentConfig() AgentConfig {
|
||||||
return AgentConfig{
|
return AgentConfig{
|
||||||
ServerURL: "http://127.0.0.1:3000",
|
ServerURL: "http://127.0.0.1:3000",
|
||||||
@ -68,7 +59,7 @@ func NewDefaultAgentConfig() AgentConfig {
|
|||||||
Enabled: true,
|
Enabled: true,
|
||||||
StateFile: "state.json",
|
StateFile: "state.json",
|
||||||
},
|
},
|
||||||
Proxy: ProxyControllerConfig{
|
Gateway: GatewayControllerConfig{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
},
|
},
|
||||||
UCI: UCIControllerConfig{
|
UCI: UCIControllerConfig{
|
||||||
@ -81,11 +72,6 @@ func NewDefaultAgentConfig() AgentConfig {
|
|||||||
DataDir: "apps/data",
|
DataDir: "apps/data",
|
||||||
DownloadDir: "apps/bundles",
|
DownloadDir: "apps/bundles",
|
||||||
},
|
},
|
||||||
SysUpgrade: SysUpgradeControllerConfig{
|
|
||||||
Enabled: true,
|
|
||||||
SysUpgradeCommand: InterpolatedStringSlice{"sysupgrade", "--force", "-u", "-v", openwrt.FirmwareFileTemplate},
|
|
||||||
FirmwareVersionCommand: InterpolatedStringSlice{"sh", "-c", `source /etc/openwrt_release && echo "$DISTRIB_ID-$DISTRIB_RELEASE-$DISTRIB_REVISION"`},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Collectors: []ShellCollectorConfig{
|
Collectors: []ShellCollectorConfig{
|
||||||
{
|
{
|
||||||
|
@ -15,6 +15,6 @@ type DatabaseConfig struct {
|
|||||||
func NewDefaultDatabaseConfig() DatabaseConfig {
|
func NewDefaultDatabaseConfig() DatabaseConfig {
|
||||||
return DatabaseConfig{
|
return DatabaseConfig{
|
||||||
Driver: "sqlite",
|
Driver: "sqlite",
|
||||||
DSN: "sqlite://emissary.sqlite?_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)&_txlock=immediate",
|
DSN: "sqlite://emissary.sqlite",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ const (
|
|||||||
|
|
||||||
type Agent struct {
|
type Agent struct {
|
||||||
ID AgentID `json:"id"`
|
ID AgentID `json:"id"`
|
||||||
Label string `json:"label"`
|
|
||||||
Thumbprint string `json:"thumbprint"`
|
Thumbprint string `json:"thumbprint"`
|
||||||
KeySet *SerializableKeySet `json:"keyset,omitempty"`
|
KeySet *SerializableKeySet `json:"keyset,omitempty"`
|
||||||
Metadata map[string]any `json:"metadata,omitempty"`
|
Metadata map[string]any `json:"metadata,omitempty"`
|
||||||
|
@ -68,7 +68,6 @@ func WithAgentQueryThumbprints(thumbprints ...string) AgentQueryOptionFunc {
|
|||||||
type AgentUpdateOptionFunc func(*AgentUpdateOptions)
|
type AgentUpdateOptionFunc func(*AgentUpdateOptions)
|
||||||
|
|
||||||
type AgentUpdateOptions struct {
|
type AgentUpdateOptions struct {
|
||||||
Label *string
|
|
||||||
Status *AgentStatus
|
Status *AgentStatus
|
||||||
Metadata *map[string]any
|
Metadata *map[string]any
|
||||||
KeySet *jwk.Set
|
KeySet *jwk.Set
|
||||||
@ -98,9 +97,3 @@ func WithAgentUpdateThumbprint(thumbprint string) AgentUpdateOptionFunc {
|
|||||||
opts.Thumbprint = &thumbprint
|
opts.Thumbprint = &thumbprint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithAgentUpdateLabel(label string) AgentUpdateOptionFunc {
|
|
||||||
return func(opts *AgentUpdateOptions) {
|
|
||||||
opts.Label = &label
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -45,11 +45,7 @@ func (r *AgentRepository) GetSpecs(ctx context.Context, agentID datastore.AgentI
|
|||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer rows.Close()
|
||||||
if err := rows.Close(); err != nil {
|
|
||||||
logger.Error(ctx, "could not close rows", logger.E(errors.WithStack(err)))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
spec := &datastore.Spec{}
|
spec := &datastore.Spec{}
|
||||||
@ -65,10 +61,6 @@ func (r *AgentRepository) GetSpecs(ctx context.Context, agentID datastore.AgentI
|
|||||||
specs = append(specs, spec)
|
specs = append(specs, spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
return nil, errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return specs, nil
|
return specs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +119,7 @@ func (r *AgentRepository) Query(ctx context.Context, opts ...datastore.AgentQuer
|
|||||||
count := 0
|
count := 0
|
||||||
|
|
||||||
err := r.withTx(ctx, func(tx *sql.Tx) error {
|
err := r.withTx(ctx, func(tx *sql.Tx) error {
|
||||||
query := `SELECT id, label, thumbprint, status, created_at, updated_at FROM agents`
|
query := `SELECT id, thumbprint, status, created_at, updated_at FROM agents`
|
||||||
|
|
||||||
limit := 10
|
limit := 10
|
||||||
if options.Limit != nil {
|
if options.Limit != nil {
|
||||||
@ -184,18 +176,14 @@ func (r *AgentRepository) Query(ctx context.Context, opts ...datastore.AgentQuer
|
|||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer rows.Close()
|
||||||
if err := rows.Close(); err != nil {
|
|
||||||
logger.Error(ctx, "could not close rows", logger.E(errors.WithStack(err)))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
agent := &datastore.Agent{}
|
agent := &datastore.Agent{}
|
||||||
|
|
||||||
metadata := JSONMap{}
|
metadata := JSONMap{}
|
||||||
|
|
||||||
if err := rows.Scan(&agent.ID, &agent.Label, &agent.Thumbprint, &agent.Status, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
|
if err := rows.Scan(&agent.ID, &agent.Thumbprint, &agent.Status, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,10 +192,6 @@ func (r *AgentRepository) Query(ctx context.Context, opts ...datastore.AgentQuer
|
|||||||
agents = append(agents, agent)
|
agents = append(agents, agent)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
row := tx.QueryRowContext(ctx, `SELECT count(id) FROM agents `+filters, args...)
|
row := tx.QueryRowContext(ctx, `SELECT count(id) FROM agents `+filters, args...)
|
||||||
if err := row.Scan(&count); err != nil {
|
if err := row.Scan(&count); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
@ -285,21 +269,9 @@ func (r *AgentRepository) Create(ctx context.Context, thumbprint string, keySet
|
|||||||
|
|
||||||
// Delete implements datastore.AgentRepository
|
// Delete implements datastore.AgentRepository
|
||||||
func (r *AgentRepository) Delete(ctx context.Context, id datastore.AgentID) error {
|
func (r *AgentRepository) Delete(ctx context.Context, id datastore.AgentID) error {
|
||||||
err := r.withTx(ctx, func(tx *sql.Tx) error {
|
query := `DELETE FROM agents WHERE id = $1`
|
||||||
query := `DELETE FROM agents WHERE id = $1`
|
|
||||||
_, err := r.db.ExecContext(ctx, query, id)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
query = `DELETE FROM specs WHERE agent_id = $1`
|
_, err := r.db.ExecContext(ctx, query, id)
|
||||||
_, err = r.db.ExecContext(ctx, query, id)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
@ -315,7 +287,7 @@ func (r *AgentRepository) Get(ctx context.Context, id datastore.AgentID) (*datas
|
|||||||
|
|
||||||
err := r.withTx(ctx, func(tx *sql.Tx) error {
|
err := r.withTx(ctx, func(tx *sql.Tx) error {
|
||||||
query := `
|
query := `
|
||||||
SELECT "id", "label", "thumbprint", "keyset", "metadata", "status", "created_at", "updated_at"
|
SELECT "id", "thumbprint", "keyset", "metadata", "status", "created_at", "updated_at"
|
||||||
FROM agents
|
FROM agents
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
`
|
`
|
||||||
@ -325,7 +297,7 @@ func (r *AgentRepository) Get(ctx context.Context, id datastore.AgentID) (*datas
|
|||||||
metadata := JSONMap{}
|
metadata := JSONMap{}
|
||||||
var rawKeySet []byte
|
var rawKeySet []byte
|
||||||
|
|
||||||
if err := row.Scan(&agent.ID, &agent.Label, &agent.Thumbprint, &rawKeySet, &metadata, &agent.Status, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
|
if err := row.Scan(&agent.ID, &agent.Thumbprint, &rawKeySet, &metadata, &agent.Status, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return datastore.ErrNotFound
|
return datastore.ErrNotFound
|
||||||
}
|
}
|
||||||
@ -395,12 +367,6 @@ func (r *AgentRepository) Update(ctx context.Context, id datastore.AgentID, opts
|
|||||||
index++
|
index++
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.Label != nil {
|
|
||||||
query += fmt.Sprintf(`, label = $%d`, index)
|
|
||||||
args = append(args, *options.Label)
|
|
||||||
index++
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.Metadata != nil {
|
if options.Metadata != nil {
|
||||||
query += fmt.Sprintf(`, metadata = $%d`, index)
|
query += fmt.Sprintf(`, metadata = $%d`, index)
|
||||||
args = append(args, JSONMap(*options.Metadata))
|
args = append(args, JSONMap(*options.Metadata))
|
||||||
@ -409,7 +375,7 @@ func (r *AgentRepository) Update(ctx context.Context, id datastore.AgentID, opts
|
|||||||
|
|
||||||
query += `
|
query += `
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
RETURNING "id", "label", "thumbprint", "keyset", "metadata", "status", "created_at", "updated_at"
|
RETURNING "id", "thumbprint", "keyset", "metadata", "status", "created_at", "updated_at"
|
||||||
`
|
`
|
||||||
|
|
||||||
row := tx.QueryRowContext(ctx, query, args...)
|
row := tx.QueryRowContext(ctx, query, args...)
|
||||||
@ -417,7 +383,7 @@ func (r *AgentRepository) Update(ctx context.Context, id datastore.AgentID, opts
|
|||||||
metadata := JSONMap{}
|
metadata := JSONMap{}
|
||||||
var rawKeySet []byte
|
var rawKeySet []byte
|
||||||
|
|
||||||
if err := row.Scan(&agent.ID, &agent.Label, &agent.Thumbprint, &rawKeySet, &metadata, &agent.Status, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
|
if err := row.Scan(&agent.ID, &agent.Thumbprint, &rawKeySet, &metadata, &agent.Status, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return datastore.ErrNotFound
|
return datastore.ErrNotFound
|
||||||
}
|
}
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
package format
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/format/json"
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/format/table"
|
|
||||||
)
|
|
@ -1,6 +0,0 @@
|
|||||||
package passwd
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "forge.cadoles.com/arcad/edge/pkg/module/auth/http/passwd/argon2id"
|
|
||||||
_ "forge.cadoles.com/arcad/edge/pkg/module/auth/http/passwd/plain"
|
|
||||||
)
|
|
@ -1,8 +0,0 @@
|
|||||||
package spec
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/agent/controller/app/spec"
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/agent/controller/openwrt/spec/sysupgrade"
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/proxy"
|
|
||||||
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/uci"
|
|
||||||
)
|
|
@ -1,6 +0,0 @@
|
|||||||
package sql
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "github.com/jackc/pgx/v5/stdlib"
|
|
||||||
_ "modernc.org/sqlite"
|
|
||||||
)
|
|
@ -23,13 +23,6 @@ type (
|
|||||||
ParseOption = jwk.ParseOption
|
ParseOption = jwk.ParseOption
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
FromRaw = jwk.FromRaw
|
|
||||||
NewSet = jwk.NewSet
|
|
||||||
)
|
|
||||||
|
|
||||||
const AlgorithmKey = jwk.AlgorithmKey
|
|
||||||
|
|
||||||
func Parse(src []byte, options ...jwk.ParseOption) (Set, error) {
|
func Parse(src []byte, options ...jwk.ParseOption) (Set, error) {
|
||||||
return jwk.Parse(src, options...)
|
return jwk.Parse(src, options...)
|
||||||
}
|
}
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/proxy/wildcard"
|
|
||||||
)
|
|
||||||
|
|
||||||
func FilterHosts(allowedHostPatterns ...string) Middleware {
|
|
||||||
return func(h http.Handler) http.Handler {
|
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if matches := wildcard.MatchAny(r.Host, allowedHostPatterns...); !matches {
|
|
||||||
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return http.HandlerFunc(fn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithAllowedHosts(allowedHostPatterns ...string) OptionFunc {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Middlewares = append(o.Middlewares, FilterHosts(allowedHostPatterns...))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/proxy/wildcard"
|
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
func RewriteHosts(mappings map[string]*url.URL) Middleware {
|
|
||||||
patterns := make([]string, len(mappings))
|
|
||||||
|
|
||||||
for p := range mappings {
|
|
||||||
patterns = append(patterns, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(patterns)
|
|
||||||
reverse(patterns)
|
|
||||||
|
|
||||||
return func(h http.Handler) http.Handler {
|
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := r.Context()
|
|
||||||
|
|
||||||
var match *url.URL
|
|
||||||
|
|
||||||
for _, p := range patterns {
|
|
||||||
logger.Debug(ctx, "matching host to pattern", logger.F("host", r.Host), logger.F("pattern", p))
|
|
||||||
|
|
||||||
if matches := wildcard.Match(r.Host, p); !matches {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
match = mappings[p]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if match == nil {
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = logger.With(ctx, logger.F("originalHost", r.Host))
|
|
||||||
r = r.WithContext(ctx)
|
|
||||||
|
|
||||||
originalURL := r.URL.String()
|
|
||||||
|
|
||||||
r.URL.Host = match.Host
|
|
||||||
r.URL.Scheme = match.Scheme
|
|
||||||
|
|
||||||
logger.Debug(ctx, "rewriting url", logger.F("from", originalURL), logger.F("to", r.URL.String()))
|
|
||||||
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return http.HandlerFunc(fn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithRewriteHosts(mappings map[string]*url.URL) OptionFunc {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Middlewares = append(o.Middlewares, RewriteHosts(mappings))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package proxy
|
|
||||||
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
type Middleware func(h http.Handler) http.Handler
|
|
||||||
|
|
||||||
type ProxyResponseTransformer interface {
|
|
||||||
TransformResponse(*http.Response) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type defaultProxyResponseTransformer struct{}
|
|
||||||
|
|
||||||
// TransformResponse implements ProxyResponseTransformer
|
|
||||||
func (*defaultProxyResponseTransformer) TransformResponse(*http.Response) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ ProxyResponseTransformer = &defaultProxyResponseTransformer{}
|
|
||||||
|
|
||||||
type ProxyResponseMiddleware func(ProxyResponseTransformer) ProxyResponseTransformer
|
|
||||||
|
|
||||||
type ProxyRequestTransformer interface {
|
|
||||||
TransformRequest(*http.Request)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProxyRequestMiddleware func(ProxyRequestTransformer) ProxyRequestTransformer
|
|
||||||
|
|
||||||
type defaultProxyRequestTransformer struct{}
|
|
||||||
|
|
||||||
// TransformRequest implements ProxyRequestTransformer
|
|
||||||
func (*defaultProxyRequestTransformer) TransformRequest(*http.Request) {}
|
|
||||||
|
|
||||||
var _ ProxyRequestTransformer = &defaultProxyRequestTransformer{}
|
|
@ -1,29 +0,0 @@
|
|||||||
package proxy
|
|
||||||
|
|
||||||
type Options struct {
|
|
||||||
Middlewares []Middleware
|
|
||||||
ProxyRequestMiddlewares []ProxyRequestMiddleware
|
|
||||||
ProxyResponseMiddlewares []ProxyResponseMiddleware
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultOptions() *Options {
|
|
||||||
return &Options{
|
|
||||||
Middlewares: make([]Middleware, 0),
|
|
||||||
ProxyRequestMiddlewares: make([]ProxyRequestMiddleware, 0),
|
|
||||||
ProxyResponseMiddlewares: make([]ProxyResponseMiddleware, 0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type OptionFunc func(*Options)
|
|
||||||
|
|
||||||
func WithProxyRequestMiddlewares(middlewares ...ProxyRequestMiddleware) OptionFunc {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.ProxyRequestMiddlewares = middlewares
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithproxyResponseMiddlewares(middlewares ...ProxyResponseMiddleware) OptionFunc {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.ProxyResponseMiddlewares = middlewares
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,131 +0,0 @@
|
|||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httputil"
|
|
||||||
"net/url"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Proxy struct {
|
|
||||||
reversers sync.Map
|
|
||||||
handler http.Handler
|
|
||||||
proxyResponseTransformer ProxyResponseTransformer
|
|
||||||
proxyRequestTransformer ProxyRequestTransformer
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeHTTP implements http.Handler
|
|
||||||
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
p.handler.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Proxy) proxyRequest(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := r.Context()
|
|
||||||
|
|
||||||
var reverser *httputil.ReverseProxy
|
|
||||||
|
|
||||||
key := fmt.Sprintf("%s://%s", r.URL.Scheme, r.URL.Host)
|
|
||||||
|
|
||||||
createAndStore := func() {
|
|
||||||
target := &url.URL{
|
|
||||||
Scheme: r.URL.Scheme,
|
|
||||||
Host: r.URL.Host,
|
|
||||||
}
|
|
||||||
|
|
||||||
reverser = httputil.NewSingleHostReverseProxy(target)
|
|
||||||
|
|
||||||
originalDirector := reverser.Director
|
|
||||||
|
|
||||||
if p.proxyRequestTransformer != nil {
|
|
||||||
reverser.Director = func(r *http.Request) {
|
|
||||||
originalURL := r.URL.String()
|
|
||||||
originalDirector(r)
|
|
||||||
p.proxyRequestTransformer.TransformRequest(r)
|
|
||||||
logger.Debug(ctx, "proxying request", logger.F("targetURL", r.URL.String()), logger.F("originalURL", originalURL))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.proxyResponseTransformer != nil {
|
|
||||||
reverser.ModifyResponse = func(r *http.Response) error {
|
|
||||||
if err := p.proxyResponseTransformer.TransformResponse(r); err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.reversers.Store(key, reverser)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, exists := p.reversers.Load(key)
|
|
||||||
if !exists {
|
|
||||||
createAndStore()
|
|
||||||
}
|
|
||||||
|
|
||||||
reverser, ok := raw.(*httputil.ReverseProxy)
|
|
||||||
if !ok {
|
|
||||||
createAndStore()
|
|
||||||
}
|
|
||||||
|
|
||||||
reverser.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(funcs ...OptionFunc) *Proxy {
|
|
||||||
opts := defaultOptions()
|
|
||||||
for _, fn := range funcs {
|
|
||||||
fn(opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
proxy := &Proxy{}
|
|
||||||
|
|
||||||
handler := http.HandlerFunc(proxy.proxyRequest)
|
|
||||||
proxy.handler = createMiddlewareChain(handler, opts.Middlewares)
|
|
||||||
|
|
||||||
proxy.proxyRequestTransformer = createProxyRequestChain(&defaultProxyRequestTransformer{}, opts.ProxyRequestMiddlewares)
|
|
||||||
proxy.proxyResponseTransformer = createProxyResponseChain(&defaultProxyResponseTransformer{}, opts.ProxyResponseMiddlewares)
|
|
||||||
|
|
||||||
return proxy
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ http.Handler = &Proxy{}
|
|
||||||
|
|
||||||
func createMiddlewareChain(handler http.Handler, middlewares []Middleware) http.Handler {
|
|
||||||
reverse(middlewares)
|
|
||||||
|
|
||||||
for _, m := range middlewares {
|
|
||||||
handler = m(handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
return handler
|
|
||||||
}
|
|
||||||
|
|
||||||
func createProxyResponseChain(transformer ProxyResponseTransformer, middlewares []ProxyResponseMiddleware) ProxyResponseTransformer {
|
|
||||||
reverse(middlewares)
|
|
||||||
|
|
||||||
for _, m := range middlewares {
|
|
||||||
transformer = m(transformer)
|
|
||||||
}
|
|
||||||
|
|
||||||
return transformer
|
|
||||||
}
|
|
||||||
|
|
||||||
func createProxyRequestChain(transformer ProxyRequestTransformer, middlewares []ProxyRequestMiddleware) ProxyRequestTransformer {
|
|
||||||
reverse(middlewares)
|
|
||||||
|
|
||||||
for _, m := range middlewares {
|
|
||||||
transformer = m(transformer)
|
|
||||||
}
|
|
||||||
|
|
||||||
return transformer
|
|
||||||
}
|
|
||||||
|
|
||||||
func reverse[S ~[]E, E any](s S) {
|
|
||||||
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
s[i], s[j] = s[j], s[i]
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
package wildcard
|
|
||||||
|
|
||||||
const wildcard = '*'
|
|
||||||
|
|
||||||
func Match(str, pattern string) bool {
|
|
||||||
if pattern == "" {
|
|
||||||
return str == pattern
|
|
||||||
}
|
|
||||||
|
|
||||||
if pattern == string(wildcard) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return deepMatchRune([]rune(str), []rune(pattern))
|
|
||||||
}
|
|
||||||
|
|
||||||
func MatchAny(str string, patterns ...string) bool {
|
|
||||||
for _, p := range patterns {
|
|
||||||
if matches := Match(str, p); matches {
|
|
||||||
return matches
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func deepMatchRune(str, pattern []rune) bool {
|
|
||||||
for len(pattern) > 0 {
|
|
||||||
switch pattern[0] {
|
|
||||||
default:
|
|
||||||
if len(str) == 0 || str[0] != pattern[0] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
case wildcard:
|
|
||||||
return deepMatchRune(str, pattern[1:]) ||
|
|
||||||
(len(str) > 0 && deepMatchRune(str[1:], pattern))
|
|
||||||
}
|
|
||||||
|
|
||||||
str = str[1:]
|
|
||||||
pattern = pattern[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
return len(str) == 0 && len(pattern) == 0
|
|
||||||
}
|
|
@ -16,10 +16,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ErrCodeUnknownError api.ErrorCode = "unknown-error"
|
ErrCodeUnknownError api.ErrorCode = "unknown-error"
|
||||||
ErrCodeNotFound api.ErrorCode = "not-found"
|
ErrCodeNotFound api.ErrorCode = "not-found"
|
||||||
ErrCodeInvalidSignature api.ErrorCode = "invalid-signature"
|
ErrInvalidSignature api.ErrorCode = "invalid-signature"
|
||||||
ErrCodeConflict api.ErrorCode = "conflict"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type registerAgentRequest struct {
|
type registerAgentRequest struct {
|
||||||
@ -47,8 +46,6 @@ func (s *Server) registerAgent(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
ctx = logger.With(ctx, logger.F("agentThumbprint", registerAgentReq.Thumbprint))
|
ctx = logger.With(ctx, logger.F("agentThumbprint", registerAgentReq.Thumbprint))
|
||||||
|
|
||||||
// Validate that the existing signature validates the request
|
|
||||||
|
|
||||||
validSignature, err := jwk.Verify(keySet, registerAgentReq.Signature, registerAgentReq.Thumbprint, registerAgentReq.Metadata)
|
validSignature, err := jwk.Verify(keySet, registerAgentReq.Signature, registerAgentReq.Thumbprint, registerAgentReq.Metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(ctx, "could not validate signature", logger.E(errors.WithStack(err)))
|
logger.Error(ctx, "could not validate signature", logger.E(errors.WithStack(err)))
|
||||||
@ -58,8 +55,8 @@ func (s *Server) registerAgent(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !validSignature {
|
if !validSignature {
|
||||||
logger.Error(ctx, "conflicting signature", logger.F("signature", registerAgentReq.Signature))
|
logger.Error(ctx, "invalid signature", logger.F("signature", registerAgentReq.Signature))
|
||||||
api.ErrorResponse(w, http.StatusConflict, ErrCodeConflict, nil)
|
api.ErrorResponse(w, http.StatusBadRequest, ErrInvalidSignature, nil)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -100,28 +97,6 @@ func (s *Server) registerAgent(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
agentID := agents[0].ID
|
|
||||||
|
|
||||||
agent, err = s.agentRepo.Get(ctx, agentID)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(
|
|
||||||
ctx, "could not retrieve agent",
|
|
||||||
logger.E(errors.WithStack(err)), logger.F("agentID", agentID),
|
|
||||||
)
|
|
||||||
api.ErrorResponse(w, http.StatusInternalServerError, ErrCodeUnknownError, nil)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
validSignature, err = jwk.Verify(agent.KeySet.Set, registerAgentReq.Signature, registerAgentReq.Thumbprint, registerAgentReq.Metadata)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(ctx, "could not validate signature using previous keyset", logger.E(errors.WithStack(err)))
|
|
||||||
|
|
||||||
api.ErrorResponse(w, http.StatusConflict, ErrCodeConflict, nil)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
agent, err = s.agentRepo.Update(
|
agent, err = s.agentRepo.Update(
|
||||||
ctx, agents[0].ID,
|
ctx, agents[0].ID,
|
||||||
datastore.WithAgentUpdateKeySet(keySet),
|
datastore.WithAgentUpdateKeySet(keySet),
|
||||||
@ -145,7 +120,6 @@ func (s *Server) registerAgent(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
type updateAgentRequest struct {
|
type updateAgentRequest struct {
|
||||||
Status *datastore.AgentStatus `json:"status" validate:"omitempty,oneof=0 1 2 3"`
|
Status *datastore.AgentStatus `json:"status" validate:"omitempty,oneof=0 1 2 3"`
|
||||||
Label *string `json:"label" validate:"omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) updateAgent(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) updateAgent(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -167,10 +141,6 @@ func (s *Server) updateAgent(w http.ResponseWriter, r *http.Request) {
|
|||||||
options = append(options, datastore.WithAgentUpdateStatus(*updateAgentReq.Status))
|
options = append(options, datastore.WithAgentUpdateStatus(*updateAgentReq.Status))
|
||||||
}
|
}
|
||||||
|
|
||||||
if updateAgentReq.Label != nil {
|
|
||||||
options = append(options, datastore.WithAgentUpdateLabel(*updateAgentReq.Label))
|
|
||||||
}
|
|
||||||
|
|
||||||
agent, err := s.agentRepo.Update(
|
agent, err := s.agentRepo.Update(
|
||||||
ctx,
|
ctx,
|
||||||
datastore.AgentID(agentID),
|
datastore.AgentID(agentID),
|
||||||
|
@ -1,155 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/auth"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/auth/agent"
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/auth/thirdparty"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"gitlab.com/wpetit/goweb/api"
|
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ErrCodeForbidden api.ErrorCode = "forbidden"
|
|
||||||
|
|
||||||
func assertGlobalReadAccess(h http.Handler) http.Handler {
|
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
reqUser, ok := assertRequestUser(w, r)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch user := reqUser.(type) {
|
|
||||||
case *thirdparty.User:
|
|
||||||
role := user.Role()
|
|
||||||
if role == thirdparty.RoleReader || role == thirdparty.RoleWriter {
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
case *agent.User:
|
|
||||||
// Agents dont have global read access
|
|
||||||
|
|
||||||
default:
|
|
||||||
logUnexpectedUserType(r.Context(), reqUser)
|
|
||||||
}
|
|
||||||
|
|
||||||
forbidden(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return http.HandlerFunc(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertAgentWriteAccess(h http.Handler) http.Handler {
|
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
reqUser, ok := assertRequestUser(w, r)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
agentID, ok := getAgentID(w, r)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch user := reqUser.(type) {
|
|
||||||
case *thirdparty.User:
|
|
||||||
role := user.Role()
|
|
||||||
if role == thirdparty.RoleWriter {
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
case *agent.User:
|
|
||||||
if user.Agent().ID == agentID {
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
logUnexpectedUserType(r.Context(), reqUser)
|
|
||||||
}
|
|
||||||
|
|
||||||
forbidden(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return http.HandlerFunc(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertAgentReadAccess(h http.Handler) http.Handler {
|
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
reqUser, ok := assertRequestUser(w, r)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
agentID, ok := getAgentID(w, r)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch user := reqUser.(type) {
|
|
||||||
case *thirdparty.User:
|
|
||||||
role := user.Role()
|
|
||||||
if role == thirdparty.RoleReader || role == thirdparty.RoleWriter {
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
case *agent.User:
|
|
||||||
if user.Agent().ID == agentID {
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
logUnexpectedUserType(r.Context(), reqUser)
|
|
||||||
}
|
|
||||||
|
|
||||||
forbidden(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return http.HandlerFunc(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertRequestUser(w http.ResponseWriter, r *http.Request) (auth.User, bool) {
|
|
||||||
ctx := r.Context()
|
|
||||||
user, err := auth.CtxUser(ctx)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(ctx, "could not retrieve user", logger.E(errors.WithStack(err)))
|
|
||||||
|
|
||||||
forbidden(w, r)
|
|
||||||
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
if user == nil {
|
|
||||||
forbidden(w, r)
|
|
||||||
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return user, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func forbidden(w http.ResponseWriter, r *http.Request) {
|
|
||||||
logger.Warn(r.Context(), "forbidden", logger.F("path", r.URL.Path))
|
|
||||||
|
|
||||||
api.ErrorResponse(w, http.StatusForbidden, ErrCodeForbidden, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func logUnexpectedUserType(ctx context.Context, user auth.User) {
|
|
||||||
logger.Error(
|
|
||||||
ctx, "unexpected user type",
|
|
||||||
logger.F("subject", user.Subject()),
|
|
||||||
logger.F("type", fmt.Sprintf("%T", user)),
|
|
||||||
)
|
|
||||||
}
|
|
@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/auth"
|
"forge.cadoles.com/Cadoles/emissary/internal/auth"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/auth/agent"
|
"forge.cadoles.com/Cadoles/emissary/internal/auth/agent"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/auth/thirdparty"
|
"forge.cadoles.com/Cadoles/emissary/internal/auth/user"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/config"
|
"forge.cadoles.com/Cadoles/emissary/internal/config"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/jwk"
|
"forge.cadoles.com/Cadoles/emissary/internal/jwk"
|
||||||
@ -105,19 +105,19 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e
|
|||||||
|
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
r.Use(auth.Middleware(
|
r.Use(auth.Middleware(
|
||||||
thirdparty.NewAuthenticator(keys, string(s.conf.Issuer)),
|
user.NewAuthenticator(keys, string(s.conf.Issuer)),
|
||||||
agent.NewAuthenticator(s.agentRepo),
|
agent.NewAuthenticator(s.agentRepo),
|
||||||
))
|
))
|
||||||
|
|
||||||
r.Route("/agents", func(r chi.Router) {
|
r.Route("/agents", func(r chi.Router) {
|
||||||
r.With(assertGlobalReadAccess).Get("/", s.queryAgents)
|
r.Get("/", s.queryAgents)
|
||||||
r.With(assertAgentReadAccess).Get("/{agentID}", s.getAgent)
|
r.Get("/{agentID}", s.getAgent)
|
||||||
r.With(assertAgentWriteAccess).Put("/{agentID}", s.updateAgent)
|
r.Put("/{agentID}", s.updateAgent)
|
||||||
r.With(assertAgentWriteAccess).Delete("/{agentID}", s.deleteAgent)
|
r.Delete("/{agentID}", s.deleteAgent)
|
||||||
|
|
||||||
r.With(assertAgentReadAccess).Get("/{agentID}/specs", s.getAgentSpecs)
|
r.Get("/{agentID}/specs", s.getAgentSpecs)
|
||||||
r.With(assertAgentWriteAccess).Post("/{agentID}/specs", s.updateSpec)
|
r.Post("/{agentID}/specs", s.updateSpec)
|
||||||
r.With(assertAgentWriteAccess).Delete("/{agentID}/specs", s.deleteSpec)
|
r.Delete("/{agentID}/specs", s.deleteSpec)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -10,6 +10,11 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gitlab.com/wpetit/goweb/api"
|
"gitlab.com/wpetit/goweb/api"
|
||||||
"gitlab.com/wpetit/goweb/logger"
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
|
|
||||||
|
// Import specs
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/app"
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/gateway"
|
||||||
|
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/uci"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package spec
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
@ -11,7 +11,7 @@ import (
|
|||||||
var schema []byte
|
var schema []byte
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
if err := spec.Register(Name, schema); err != nil {
|
if err := spec.Register(NameApp, schema); err != nil {
|
||||||
panic(errors.WithStack(err))
|
panic(errors.WithStack(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
36
internal/spec/app/schema.json
Normal file
36
internal/spec/app/schema.json
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$id": "https://app.emissary.cadoles.com/spec.json",
|
||||||
|
"title": "AppSpec",
|
||||||
|
"description": "Emissary 'App' specification",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"apps": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
".*": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sha256sum": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"address": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"format": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["zip", "tar.gz"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["url", "sha256sum", "address", "format"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["apps"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
@ -1,20 +1,25 @@
|
|||||||
package sysupgrade
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
||||||
)
|
)
|
||||||
|
|
||||||
const Name spec.Name = "sysupgrade.openwrt.emissary.cadoles.com"
|
const NameApp spec.Name = "app.emissary.cadoles.com"
|
||||||
|
|
||||||
type Spec struct {
|
type Spec struct {
|
||||||
Revision int `json:"revision"`
|
Revision int `json:"revisions"`
|
||||||
|
Apps map[string]AppEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppEntry struct {
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
SHA256Sum string `json:"sha256sum"`
|
SHA256Sum string `json:"sha256sum"`
|
||||||
Version string `json:"version"`
|
Address string `json:"address"`
|
||||||
|
Format string `json:"format"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Spec) SpecName() spec.Name {
|
func (s *Spec) SpecName() spec.Name {
|
||||||
return Name
|
return NameApp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Spec) SpecRevision() int {
|
func (s *Spec) SpecRevision() int {
|
||||||
@ -23,16 +28,12 @@ func (s *Spec) SpecRevision() int {
|
|||||||
|
|
||||||
func (s *Spec) SpecData() map[string]any {
|
func (s *Spec) SpecData() map[string]any {
|
||||||
return map[string]any{
|
return map[string]any{
|
||||||
"url": s.URL,
|
"apps": s.Apps,
|
||||||
"version": s.Version,
|
|
||||||
"sha256sum": s.SHA256Sum,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSpec() *Spec {
|
func NewSpec() *Spec {
|
||||||
return &Spec{
|
return &Spec{}
|
||||||
Revision: -1,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ spec.Spec = &Spec{}
|
var _ spec.Spec = &Spec{}
|
7
internal/spec/app/testdata/spec-ok.json
vendored
Normal file
7
internal/spec/app/testdata/spec-ok.json
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "app.emissary.cadoles.com",
|
||||||
|
"data": {
|
||||||
|
"apps": {}
|
||||||
|
},
|
||||||
|
"revision": 0
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package spec
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -28,7 +28,7 @@ func TestValidator(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
validator := spec.NewValidator()
|
validator := spec.NewValidator()
|
||||||
if err := validator.Register(Name, schema); err != nil {
|
if err := validator.Register(NameApp, schema); err != nil {
|
||||||
t.Fatalf("+%v", errors.WithStack(err))
|
t.Fatalf("+%v", errors.WithStack(err))
|
||||||
}
|
}
|
||||||
|
|
@ -1,28 +0,0 @@
|
|||||||
package spec
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/mitchellh/hashstructure/v2"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Equals(a Spec, b Spec) (bool, error) {
|
|
||||||
if a.SpecName() != b.SpecName() {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.SpecRevision() != b.SpecRevision() {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
hashA, err := hashstructure.Hash(a.SpecData(), hashstructure.FormatV2, nil)
|
|
||||||
if err != nil {
|
|
||||||
return false, errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hashB, err := hashstructure.Hash(b.SpecData(), hashstructure.FormatV2, nil)
|
|
||||||
if err != nil {
|
|
||||||
return false, errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return hashA == hashB, nil
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package sysupgrade
|
package gateway
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
@ -11,7 +11,7 @@ import (
|
|||||||
var schema []byte
|
var schema []byte
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
if err := spec.Register(Name, schema); err != nil {
|
if err := spec.Register(NameGateway, schema); err != nil {
|
||||||
panic(errors.WithStack(err))
|
panic(errors.WithStack(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
29
internal/spec/gateway/schema.json
Normal file
29
internal/spec/gateway/schema.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$id": "https://gateway.emissary.cadoles.com/spec.json",
|
||||||
|
"title": "GatewaySpec",
|
||||||
|
"description": "Emissary 'Gateway' specification",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"gateways": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
".*": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"address": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["address", "target"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["gateways"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
39
internal/spec/gateway/spec.go
Normal file
39
internal/spec/gateway/spec.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package gateway
|
||||||
|
|
||||||
|
import "forge.cadoles.com/Cadoles/emissary/internal/spec"
|
||||||
|
|
||||||
|
const NameGateway spec.Name = "gateway.emissary.cadoles.com"
|
||||||
|
|
||||||
|
type ID string
|
||||||
|
|
||||||
|
type Spec struct {
|
||||||
|
Revision int `json:"revision"`
|
||||||
|
Gateways map[ID]GatewayEntry `json:"gateways"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GatewayEntry struct {
|
||||||
|
Address string `json:"address"`
|
||||||
|
Target string `json:"target"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Spec) SpecName() spec.Name {
|
||||||
|
return NameGateway
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Spec) SpecRevision() int {
|
||||||
|
return s.Revision
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Spec) SpecData() map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"gateways": s.Gateways,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSpec() *Spec {
|
||||||
|
return &Spec{
|
||||||
|
Gateways: make(map[ID]GatewayEntry),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ spec.Spec = &Spec{}
|
13
internal/spec/gateway/testdata/spec-additional-prop.json
vendored
Normal file
13
internal/spec/gateway/testdata/spec-additional-prop.json
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "gateway.emissary.cadoles.com",
|
||||||
|
"data": {
|
||||||
|
"gateways": {
|
||||||
|
"cadoles.com": {
|
||||||
|
"address": ":3003",
|
||||||
|
"target": "https://www.cadoles.com",
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"revision": 0
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "proxy.emissary.cadoles.com",
|
"name": "gateway.emissary.cadoles.com",
|
||||||
"data": {
|
"data": {
|
||||||
"proxies": {
|
"gateways": {
|
||||||
"cadoles.com": {
|
"cadoles.com": {
|
||||||
"address": ":3003"
|
"address": ":3003"
|
||||||
}
|
}
|
12
internal/spec/gateway/testdata/spec-ok.json
vendored
Normal file
12
internal/spec/gateway/testdata/spec-ok.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "gateway.emissary.cadoles.com",
|
||||||
|
"data": {
|
||||||
|
"gateways": {
|
||||||
|
"cadoles.com": {
|
||||||
|
"address": ":3003",
|
||||||
|
"target": "https://www.cadoles.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"revision": 0
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package proxy
|
package gateway
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -38,7 +38,7 @@ func TestValidator(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
validator := spec.NewValidator()
|
validator := spec.NewValidator()
|
||||||
if err := validator.Register(NameProxy, schema); err != nil {
|
if err := validator.Register(NameGateway, schema); err != nil {
|
||||||
t.Fatalf("+%v", errors.WithStack(err))
|
t.Fatalf("+%v", errors.WithStack(err))
|
||||||
}
|
}
|
||||||
|
|
@ -1,17 +0,0 @@
|
|||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed schema.json
|
|
||||||
var schema []byte
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if err := spec.Register(NameProxy, schema); err != nil {
|
|
||||||
panic(errors.WithStack(err))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
||||||
"$id": "https://proxy.emissary.cadoles.com/spec.json",
|
|
||||||
"title": "ProxySpec",
|
|
||||||
"description": "Emissary 'Proxy' specification",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"proxies": {
|
|
||||||
"type": "object",
|
|
||||||
"patternProperties": {
|
|
||||||
".*": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"address": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"mappings": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"hostPattern": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"target": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["hostPattern", "target"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["address", "mappings"],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["proxies"],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
package proxy
|
|
||||||
|
|
||||||
import "forge.cadoles.com/Cadoles/emissary/internal/spec"
|
|
||||||
|
|
||||||
const NameProxy spec.Name = "proxy.emissary.cadoles.com"
|
|
||||||
|
|
||||||
type ID string
|
|
||||||
|
|
||||||
type Spec struct {
|
|
||||||
Revision int `json:"revision"`
|
|
||||||
Proxies map[ID]ProxyEntry `json:"proxies"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProxyEntry struct {
|
|
||||||
Address string `json:"address"`
|
|
||||||
Mappings []ProxyMapping `json:"mappings"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProxyMapping struct {
|
|
||||||
HostPattern string `json:"hostPattern"`
|
|
||||||
Target string `json:"target"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Spec) SpecName() spec.Name {
|
|
||||||
return NameProxy
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Spec) SpecRevision() int {
|
|
||||||
return s.Revision
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Spec) SpecData() map[string]any {
|
|
||||||
return map[string]any{
|
|
||||||
"proxies": s.Proxies,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSpec() *Spec {
|
|
||||||
return &Spec{
|
|
||||||
Revision: -1,
|
|
||||||
Proxies: make(map[ID]ProxyEntry),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ spec.Spec = &Spec{}
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user