Merge pull request #566 from openziti/release-next

Release 0.24.2
This commit is contained in:
Paul Lorenz
2021-12-13 14:21:17 -05:00
committed by GitHub
15 changed files with 830 additions and 1018 deletions
+9
View File
@@ -1,3 +1,12 @@
# Release 0.24.2
## What's New
* Bug fix: link verification could panic if link was established before control was finished establishing
* Bug fix: When checking edge terminator validity in the router, check terminator id as well the address
* Enhancement: Improve logging around links in routers. Ensure we close both channels when closing a split link
* Enhancement: Add support for inspect in `ziti fabric`. Works the same as `ziti-fabric inspect`
# Release 0.24.1
## What's New
+70
View File
@@ -0,0 +1,70 @@
FROM ubuntu:18.04
#
# this file mirrors the build params used in the GitHub Actions and enables
# reproducible builds for downstream forks and Ziti contributors
#
ARG latest_golang=1.17.2
ARG go_distribution_file=go${latest_golang}.linux-amd64.tar.gz
ARG GOPATH=/root/go
ARG GOROOT=/usr/local/go
RUN apt-get update
RUN apt-get -yq install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf gcc-aarch64-linux-gnu
RUN apt-get -yq install wget build-essential
RUN wget -q https://golang.org/dl/${go_distribution_file}
RUN tar -xzf ${go_distribution_file} -C /usr/local/
RUN mkdir ${GOPATH}
ENV GOPATH=${GOPATH}
ENV GOROOT=${GOROOT}
ENV PATH=$GOPATH/bin:$GOROOT/bin:$PATH
RUN go install github.com/mitchellh/gox@latest
WORKDIR /mnt
CMD ["./linux-build.sh"]
##
# build this Dockerfile
##
#
# MINIMUM_GOLANG=$(grep -Po '^go\s+\K\d+\.\d+(\.\d+)?$' go.mod)
# LATEST_GOLANG=$(curl -sSf "https://golang.org/VERSION?m=text" | /bin/grep -Po '^go(\s+)?\K\d+\.\d+\.\d+$')
# docker build --tag=zitibuilder --file=Dockerfile.linux-build --build-arg latest_golang=${LATEST_GOLANG} .
##
# build Ziti
##
# docker run --rm --name=zitibuilder --volume=$PWD:/mnt zitibuilder
# $ tree ./release
# ./release
# ├── amd64
# │   └── linux
# │   ├── ziti
# │   ├── ziti-controller
# │   ├── ziti-fabric
# │   ├── ziti-fabric-gw
# │   ├── ziti-fabric-test
# │   ├── ziti-probe
# │   ├── ziti-router
# │   └── ziti-tunnel
# ├── arm
# │   └── linux
# │   ├── ziti
# │   ├── ziti-controller
# │   ├── ziti-fabric
# │   ├── ziti-fabric-gw
# │   ├── ziti-fabric-test
# │   ├── ziti-probe
# │   ├── ziti-router
# │   └── ziti-tunnel
# └── arm64
# └── linux
# ├── ziti
# ├── ziti-controller
# ├── ziti-fabric
# ├── ziti-fabric-gw
# ├── ziti-fabric-test
# ├── ziti-probe
# ├── ziti-router
# └── ziti-tunnel
# 6 directories, 24 files
+7 -8
View File
@@ -14,11 +14,10 @@ go 1.17
//replace github.com/michaelquigley/pfxlog => ../pfxlog
replace go.etcd.io/bbolt => github.com/openziti/bbolt v1.3.6-0.20210317142109-547da822475e
//replace go.etcd.io/bbolt => github.com/openziti/bbolt v1.3.6-0.20210317142109-547da822475e
require (
github.com/Jeffail/gabs v1.4.0
github.com/Jeffail/gabs/v2 v2.6.1
github.com/MakeNowJust/heredoc v1.0.0
github.com/MichaelMure/go-term-markdown v0.1.4
github.com/alecthomas/chroma v0.9.2
@@ -34,10 +33,10 @@ require (
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d
github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19
github.com/michaelquigley/pfxlog v0.6.3
github.com/openziti/edge v0.21.3
github.com/openziti/fabric v0.17.2
github.com/openziti/foundation v0.16.7
github.com/openziti/sdk-golang v0.15.117
github.com/openziti/edge v0.21.16
github.com/openziti/fabric v0.17.9
github.com/openziti/foundation v0.16.9
github.com/openziti/sdk-golang v0.15.122
github.com/pborman/uuid v1.2.0
github.com/pkg/errors v0.9.1
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0
@@ -67,7 +66,7 @@ require (
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20211106181442-e4c1a74c66bd // indirect
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
github.com/biogo/store v0.0.0-20200525035639-8c94ae1e7c9c // indirect
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/coreos/go-iptables v0.6.0 // indirect
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
@@ -123,7 +122,7 @@ require (
github.com/miekg/dns v1.1.43 // indirect
github.com/miekg/pkcs11 v1.0.3 // indirect
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/mitchellh/mapstructure v1.4.2 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/natefinch/lumberjack v2.0.0+incompatible // indirect
github.com/netfoundry/secretstream v0.1.2 // indirect
github.com/nxadm/tail v1.4.8 // indirect
+16 -17
View File
@@ -66,7 +66,6 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Jeffail/gabs v1.4.0 h1://5fYRRTq1edjfIrQGvdkcd22pkYUrHZ5YC/H2GJVAo=
github.com/Jeffail/gabs v1.4.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
github.com/Jeffail/gabs/v2 v2.6.1 h1:wwbE6nTQTwIMsMxzi6XFQQYRZ6wDc1mSdxoAN+9U4Gk=
github.com/Jeffail/gabs/v2 v2.6.1/go.mod h1:xCn81vdHKxFUuWWAaD5jCTQDNPBMh5pPs9IJ+NcziBI=
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
@@ -138,8 +137,8 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBW
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
@@ -529,7 +528,7 @@ github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhK
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb-client-go/v2 v2.2.2/go.mod h1:fa/d1lAdUHxuc1jedx30ZfNG573oQTQmUni3N6pcW+0=
github.com/influxdata/influxdb-client-go/v2 v2.5.1/go.mod h1:Y/0W1+TZir7ypoQZYd2IrnVOKB3Tq6oegAQeSVN/+EU=
github.com/influxdata/influxdb-client-go/v2 v2.6.0/go.mod h1:Y/0W1+TZir7ypoQZYd2IrnVOKB3Tq6oegAQeSVN/+EU=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d h1:/WZQPMZNsjZ7IlCpsLGdQBINg5bxKQ1K1sh6awxLtkA=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
@@ -714,8 +713,8 @@ github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo=
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
@@ -761,19 +760,16 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openziti/bbolt v1.3.6-0.20210317142109-547da822475e h1:ST9+54UtCr96S5+Wa7LQtpGS6/xK7Z+KQXvOAtYgJlA=
github.com/openziti/bbolt v1.3.6-0.20210317142109-547da822475e/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
github.com/openziti/dilithium v0.3.3 h1:PLgQ6PMNLSTzCFbX/h98cmudgz/cU6TmjdSv5NAPD8k=
github.com/openziti/dilithium v0.3.3/go.mod h1:vsCjI2AU/hon9e+dLhUFbCNGesJDj2ASgkySOcpmvjo=
github.com/openziti/edge v0.21.3 h1:/cPPmvSHjpCV2S99tukD7YSe4E+MXeHtedFs+0bqOHU=
github.com/openziti/edge v0.21.3/go.mod h1:s3U82m//n12qE5rb9Ja3NQY/ZhOTwRxT9kdAahEsGnU=
github.com/openziti/fabric v0.17.1/go.mod h1:6AGi2vTZckhZ5W6DfUml8ITUh+UTq1jJabwVNVglcX0=
github.com/openziti/fabric v0.17.2 h1:xjZhTx8LGBVKOboaCFbFZwmpOIpi18ODELnNH/IutfQ=
github.com/openziti/fabric v0.17.2/go.mod h1:6AGi2vTZckhZ5W6DfUml8ITUh+UTq1jJabwVNVglcX0=
github.com/openziti/foundation v0.16.7 h1:Ryuo4wp5CeXJAZYujbXfxuo6/GPpHnvJimESNTopRFY=
github.com/openziti/foundation v0.16.7/go.mod h1:DrzmWd4wka7/RlA37KTN2q+dz9dVSH6YJ9VP6xxf28w=
github.com/openziti/sdk-golang v0.15.117 h1:XFniHPcc03YLy2ZxRz2E7GhRdiWOonNiMQDA4rjvvig=
github.com/openziti/sdk-golang v0.15.117/go.mod h1:ZI3nT0vQ5UgkvK+7P9sGYWajZSMfvq1J4DZUMZYHmPA=
github.com/openziti/edge v0.21.16 h1:IRlG6A7nuwu1KcT1LxeQHOPNEp6NPxHKV7Oo5cL5UyE=
github.com/openziti/edge v0.21.16/go.mod h1:scsApPeHqL58mza7r5T5AlvLwiZfVN3icygklKIX3fw=
github.com/openziti/fabric v0.17.9 h1:s8YmUehZXI6svwNJ01D3VyLr79AT/q470PPqT5q4qBg=
github.com/openziti/fabric v0.17.9/go.mod h1:U0oa6oD4alAiJepqPhzGRt5YZgs4aL79Lso/p2ItZqg=
github.com/openziti/foundation v0.16.9 h1:iAGPDESXRArGJQYBQSOif+fXfwYRxnvBh68tiXKB1g8=
github.com/openziti/foundation v0.16.9/go.mod h1:DrzmWd4wka7/RlA37KTN2q+dz9dVSH6YJ9VP6xxf28w=
github.com/openziti/sdk-golang v0.15.122 h1:RxFNpfsXb2wkLlvNpXUoBqABVkt/i8CnhDbk98VM6ig=
github.com/openziti/sdk-golang v0.15.122/go.mod h1:FROPdwoM1EkNxcttGtgvOZIJAHhaEe5RqSKF1Zr6aa0=
github.com/oracle/oci-go-sdk v24.2.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI=
github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231 h1:fa50YL1pzKW+1SsBnJDOHppJN9stOEwS+CRWyUtyYGU=
@@ -974,6 +970,9 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50 h1:ASw9n1EHMftwnP3Az4XW6e308+gNsrHzmdhd0Olz9Hs=
go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
Executable
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
#
# build the Linux artifacts for amd64, arm, arm64
#
# see instructions to run reproducible build with Docker in ./Dockerfile.linux-build
#
set -o pipefail -e -u
GOX_OUTPUT="release/{{.Arch}}/{{.OS}}/{{.Dir}}"
CGO_ENABLED=true gox -os=linux -arch=amd64 -output=${GOX_OUTPUT} ./...
CC=arm-linux-gnueabihf-gcc CGO_ENABLED=true gox -cgo -os=linux -arch=arm -output=${GOX_OUTPUT} ./...
CC=aarch64-linux-gnu-gcc CGO_ENABLED=true gox -cgo -os=linux -arch=arm64 -output=$GOX_OUTPUT ./...
+15
View File
@@ -94,6 +94,21 @@ services:
networks:
- zitiblue
ziti-console:
image: openziti/zac
environment:
- ZAC_SERVER_CERT_CHAIN=/openziti/pki/ziti-controller-intermediate/certs/ziti-controller-server.cert
- ZAC_SERVER_KEY=/openziti/pki/ziti-controller-intermediate/keys/ziti-controller-server.key -it
ports:
- "1408:1408"
- "8443:8443"
volumes:
- ziti-fs:/openziti
networks:
- zitiblue
- zitired
networks:
zitired:
zitiblue:
File diff suppressed because it is too large Load Diff
@@ -32,6 +32,21 @@ services:
entrypoint: /bin/bash
command: "/openziti/scripts/run-edge-router.sh edge"
ziti-console:
image: openziti/zac
environment:
- ZAC_SERVER_CERT_CHAIN=/openziti/pki/ziti-controller-intermediate/certs/ziti-controller-server.cert
- ZAC_SERVER_KEY=/openziti/pki/ziti-controller-intermediate/keys/ziti-controller-server.key -it
ports:
- "1408:1408"
- "8443:8443"
volumes:
- ziti-fs:/openziti
networks:
- zitiblue
- zitired
networks:
zitired:
zitiblue:
-323
View File
@@ -1,323 +0,0 @@
package logs
import (
"bufio"
"fmt"
"github.com/Jeffail/gabs/v2"
"github.com/openziti/foundation/util/stringz"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"os"
"regexp"
"sort"
"strings"
"time"
)
type ParseContext struct {
path string
lineNumber int
eof bool
line string
}
func ScanLines(ctx *ParseContext, callback func(ctx *ParseContext) error) error {
file, err := os.Open(ctx.path)
if err != nil {
return err
}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
ctx.line = line
if err := callback(ctx); err != nil {
return errors.Wrapf(err, "error parsing %v on line %v", ctx.path, ctx.lineNumber)
}
ctx.lineNumber++
}
ctx.eof = true
return callback(ctx)
}
type JsonParseContext struct {
ParseContext
entry *gabs.Container
}
func (self *JsonParseContext) RequiredString(path ...string) (string, error) {
v := self.entry.Search(path...)
if v == nil || v.Data() == nil {
return "", errors.Errorf("%v not found in log entry", strings.Join(path, "."))
}
s, ok := v.Data().(string)
if !ok {
return "", errors.Errorf("%v is not a string", strings.Join(path, "."))
}
return s, nil
}
func (self *JsonParseContext) ParseJsonEntry() error {
input := self.line
idx := strings.IndexByte(self.line, '{')
if idx < 0 {
fmt.Printf("WARN: non-json line: %v", self.line)
return nil
}
if idx > 0 {
input = input[idx:]
}
entry, err := gabs.ParseJSON([]byte(input))
if err != nil {
return err
}
self.entry = entry
return nil
}
func ScanJsonLines(ctx *JsonParseContext, callback func(ctx *JsonParseContext) error) error {
return ScanLines(&ctx.ParseContext, func(*ParseContext) error {
if ctx.eof {
return callback(ctx)
}
if err := ctx.ParseJsonEntry(); err != nil {
return err
}
if ctx.entry != nil {
return callback(ctx)
}
return nil
})
}
type LogMatcher interface {
Matches(ctx *JsonParseContext) (bool, error)
}
type LogFilter interface {
LogMatcher
Id() string
Label() string
Desc() string
}
type filter struct {
LogMatcher
id string
label string
desc string
}
func (self *filter) Id() string {
return self.id
}
func (self *filter) Label() string {
return self.label
}
func (self *filter) Desc() string {
return self.desc
}
type JsonLogsParser struct {
bucketSize time.Duration
currentBucket time.Time
filters []LogFilter
bucketMatches map[LogFilter]int
unmatched int
maxUnmatchedLoggedPerBucket int
ignore []string
}
func (self *JsonLogsParser) validate() error {
ids := map[string]int{}
for idx, k := range self.filters {
if v, found := ids[k.Id()]; found {
return errors.Errorf("duplicate filter id %v at indices %v and %v", k.Id(), idx, v)
}
ids[k.Id()] = idx
}
return nil
}
func (self *JsonLogsParser) ShowCategories(*cobra.Command, []string) {
for _, filter := range self.filters {
fmt.Printf("%v (%v): %v\n", filter.Id(), filter.Label(), filter.Desc())
}
}
func (self *JsonLogsParser) examineLogEntry(ctx *JsonParseContext) error {
if ctx.eof {
self.dumpBucket()
return nil
}
if err := self.bucket(ctx); err != nil {
return err
}
for _, filter := range self.filters {
match, err := filter.Matches(ctx)
if err != nil {
return err
}
if match {
current := self.bucketMatches[filter]
self.bucketMatches[filter] = current + 1
return nil
}
}
self.unmatched++
if self.unmatched <= self.maxUnmatchedLoggedPerBucket {
fmt.Printf("WARN: unmatched line: %v\n\n", ctx.line)
}
return nil
}
func (self *JsonLogsParser) bucket(ctx *JsonParseContext) error {
s, err := ctx.RequiredString("time")
if err != nil {
return err
}
t, err := time.Parse(time.RFC3339, s)
if err != nil {
return errors.Errorf("time is in an unexpected format: %v", s)
}
interval := t.Truncate(self.bucketSize)
if interval != self.currentBucket {
if !self.currentBucket.IsZero() {
self.dumpBucket()
}
self.currentBucket = interval
self.bucketMatches = map[LogFilter]int{}
self.unmatched = 0
}
return nil
}
func (self *JsonLogsParser) dumpBucket() {
var filters []LogFilter
for k := range self.bucketMatches {
if !stringz.Contains(self.ignore, k.Id()) {
filters = append(filters, k)
}
}
sort.Slice(filters, func(i, j int) bool {
return filters[i].Label() < filters[j].Label()
})
if len(filters) == 0 && self.unmatched == 0 {
return
}
fmt.Printf("%v\n---------------------------------------------------\n", self.currentBucket.Format(time.RFC3339))
for _, filter := range filters {
fmt.Printf(" %v (%v): %0000v\n", filter.Id(), filter.Label(), self.bucketMatches[filter])
}
if self.unmatched > 0 {
fmt.Printf(" unmatched: %0000v\n", self.unmatched)
}
fmt.Println()
}
func FieldStartsWith(field, substring string) LogMatcher {
return &EntryFieldStartsWithMatcher{
field: field,
prefix: substring,
}
}
type EntryFieldStartsWithMatcher struct {
field string
prefix string
}
func (self *EntryFieldStartsWithMatcher) Matches(ctx *JsonParseContext) (bool, error) {
fieldValue, err := ctx.RequiredString(self.field)
if err != nil {
return false, err
}
return strings.HasPrefix(fieldValue, self.prefix), nil
}
func FieldContains(field, substring string) LogMatcher {
return &EntryFieldContainsMatcher{
field: field,
substring: substring,
}
}
type EntryFieldContainsMatcher struct {
field string
substring string
}
func (self *EntryFieldContainsMatcher) Matches(ctx *JsonParseContext) (bool, error) {
fieldValue, err := ctx.RequiredString(self.field)
if err != nil {
return false, err
}
return strings.Contains(fieldValue, self.substring), nil
}
func FieldEquals(field, substring string) LogMatcher {
return &EntryFieldContainsMatcher{
field: field,
substring: substring,
}
}
type EntryFieldEqualsMatcher struct {
field string
value string
}
func (self *EntryFieldEqualsMatcher) Matches(ctx *JsonParseContext) (bool, error) {
fieldValue, err := ctx.RequiredString(self.field)
if err != nil {
return false, err
}
return fieldValue == self.value, nil
}
func AndMatchers(matchers ...LogMatcher) LogMatcher {
return &AndMatcher{matchers: matchers}
}
type AndMatcher struct {
matchers []LogMatcher
}
func (self *AndMatcher) Matches(ctx *JsonParseContext) (bool, error) {
for _, matcher := range self.matchers {
result, err := matcher.Matches(ctx)
if !result || err != nil {
return result, err
}
}
return true, nil
}
func FieldMatches(field, expr string) LogMatcher {
regex, err := regexp.Compile(expr)
if err != nil {
panic(err)
}
return &EntryFieldMatchesMatcher{
field: field,
regex: regex,
}
}
type EntryFieldMatchesMatcher struct {
field string
regex *regexp.Regexp
}
func (self *EntryFieldMatchesMatcher) Matches(ctx *JsonParseContext) (bool, error) {
fieldValue, err := ctx.RequiredString(self.field)
if err != nil {
return false, err
}
return self.regex.MatchString(fieldValue), nil
}
-316
View File
@@ -1,316 +0,0 @@
package logs
import (
"github.com/spf13/cobra"
"time"
)
func NewLogsCommand() *cobra.Command {
logsCmd := &cobra.Command{
Use: "logs",
Short: "commands related to analyzing various ziti component logs",
}
parseRouterLogs := &ParseRouterLogs{}
parseRouterLogs.Init()
parseRouterLogsCmd := &cobra.Command{
Use: "router",
Short: "Parse router logs",
Args: cobra.ExactArgs(1),
RunE: parseRouterLogs.run,
}
parseRouterLogsCmd.Flags().DurationVarP(&parseRouterLogs.bucketSize, "interval", "n", time.Hour, "Interval for which to aggregate log messages")
parseRouterLogsCmd.Flags().IntVarP(&parseRouterLogs.maxUnmatchedLoggedPerBucket, "max-unmatched", "u", 1, "Maximum unmatched log messages to output per bucket")
parseRouterLogsCmd.Flags().StringSliceVar(&parseRouterLogs.ignore, "ignore", []string{"IDLE", "CONF"}, "Filters to ignore")
showRouterLogCategoriesCmd := &cobra.Command{
Use: "categories",
Short: "Show router log entry categories",
Run: parseRouterLogs.ShowCategories,
}
parseRouterLogsCmd.AddCommand(showRouterLogCategoriesCmd)
logsCmd.AddCommand(parseRouterLogsCmd)
return logsCmd
}
type ParseRouterLogs struct {
JsonLogsParser
}
func (self *ParseRouterLogs) Init() {
self.filters = getRouterLogFilters()
}
func getRouterLogFilters() []LogFilter {
return []LogFilter{
&filter{
id: "IDLE",
label: "idle circuit scanner",
desc: "a circuit has been idle for at least one minute and the controller will be checked to see if the circuit is still valid",
LogMatcher: AndMatchers(
FieldContains("file", "forwarder/scanner.go"),
FieldContains("msg", " idle after "),
)},
&filter{
id: "CONF",
label: "circuit confirmation send",
desc: "controller has been notified of idle circuits and can respond if they are no longer valid",
LogMatcher: AndMatchers(
FieldContains("msg", "sent confirmation for "),
FieldContains("file", "forwarder/scanner.go"),
)},
&filter{
id: "TSCX",
label: "tunnel dial success",
desc: "a router embedded tunneler has made a successful connection to a server hosting an application",
LogMatcher: AndMatchers(
FieldStartsWith("msg", "successful connection "),
FieldContains("file", "xgress_edge_tunnel/dialer.go"),
)},
&filter{
id: "DSTX",
label: "routing destination exists",
desc: "attempting to establish a circuit, but a previous dial attempt already established the egress connection",
LogMatcher: AndMatchers(
FieldStartsWith("msg", "destination exists for "),
FieldContains("file", "handler_ctrl/route.go"),
)},
&filter{
id: "XGRF",
label: "xgress read failure",
desc: "read failure on the client or server side of the circuit; will cause the circuit to be torn down",
LogMatcher: AndMatchers(
FieldStartsWith("msg", "read failed"),
FieldContains("file", "xgress/xgress.go"),
)},
&filter{
id: "XGWF",
label: "xgress write failure",
desc: "write failure on the client or server side of the circuit; will cause the circuit to be torn down",
LogMatcher: AndMatchers(
FieldStartsWith("msg", "write failed"),
FieldContains("file", "xgress/xgress.go"),
)},
&filter{
id: "XGBF",
label: "xgress can't buffer",
desc: "while a circuit was being closed, data was received from the client or server",
LogMatcher: AndMatchers(
FieldStartsWith("msg", "failure to buffer payload"),
FieldEquals("error", "payload buffer closed"),
FieldContains("file", "xgress/xgress.go"),
)},
&filter{
id: "RCFM",
label: "can't forward",
desc: "router can't forward a message most likely because the circuit is in the middle of being torn down",
LogMatcher: AndMatchers(
FieldStartsWith("msg", "unable to forward"),
FieldContains("file", "handler_xgress/receive.go"),
)},
&filter{
id: "CSTO",
label: "circuit not started in time",
desc: "the terminating side of the circuit didn't receive a start from the initiating side",
LogMatcher: AndMatchers(
FieldEquals("msg", "xgress circuit not started in time, closing"),
FieldContains("file", "xgress/xgress.go"),
)},
&filter{
id: "TLSC",
label: "no tls certification provided",
desc: "a connection attempt was made to a TLS listener but no certificate was provided",
LogMatcher: AndMatchers(
FieldStartsWith("msg", "error receiving hello from "),
FieldContains("msg", "tls: client didn't provide a certificate"),
FieldContains("file", "channel2/classic_listener.go"),
)},
&filter{
id: "LCYT",
label: "latency timeout",
desc: "a latency ping was sent, but no response was received within the timeout",
LogMatcher: AndMatchers(
FieldStartsWith("msg", "latency timeout after "),
FieldContains("file", "metrics/latency.go"),
)},
&filter{
id: "CHRX",
label: "channel rx error",
desc: "channel read failed",
LogMatcher: AndMatchers(
FieldStartsWith("msg", "rx error"),
FieldContains("msg", "connection reset by peer"),
FieldContains("file", "channel2/impl.go"),
)},
&filter{
id: "LNKS",
label: "dialing split channel link",
desc: "a link is being dialed to another router with separate connections for data and acknowledgements",
LogMatcher: AndMatchers(
FieldStartsWith("msg", "dialing link with split payload/ack channels"),
FieldContains("file", "xlink_transport/dialer.go"),
)},
&filter{
id: "LNKR",
label: "controller requesting dial",
desc: "received a request from the controller to dial another router",
LogMatcher: AndMatchers(
FieldStartsWith("msg", "received link connect request"),
FieldContains("file", "handler_ctrl/dial.go"),
)},
&filter{
id: "LNKD",
label: "dialing link",
desc: "dialing another router to establish a link",
LogMatcher: AndMatchers(
FieldEquals("msg", "dialing link"),
FieldContains("file", "handler_ctrl/dial.go"),
)},
&filter{
id: "LNKE",
label: "link established",
desc: "a link to another router has been established",
LogMatcher: AndMatchers(
FieldEquals("msg", "link established"),
FieldContains("file", "handler_ctrl/dial.go"),
)},
&filter{
id: "LNKP",
label: "dialing link payload channel",
desc: "dialing the link payload channel of a link",
LogMatcher: AndMatchers(
FieldStartsWith("msg", "dialing payload channel for"),
FieldContains("file", "xlink_transport/dialer.go"),
)},
&filter{
id: "LNKK",
label: "dialing link ack channel",
desc: "dialing the link ack channel of a link",
LogMatcher: AndMatchers(
FieldStartsWith("msg", "dialing ack channel for"),
FieldContains("file", "xlink_transport/dialer.go"),
)},
&filter{
id: "LNKC",
label: "link closed",
desc: "a router to router link was closed",
LogMatcher: AndMatchers(
FieldEquals("msg", "link closed"),
FieldContains("file", "handler_link/close.go"),
)},
&filter{
id: "FLTS",
label: "link fault sent",
desc: "the router notified the controller that a link failed",
LogMatcher: AndMatchers(
FieldEquals("msg", "transmitted link fault"),
FieldContains("file", "handler_link/close.go"),
)},
&filter{
id: "LNKA",
label: "link accepted",
desc: "another router has dialed this router and a link has been established",
LogMatcher: AndMatchers(
FieldStartsWith("msg", "accepted new link"),
FieldContains("file", "router/accepter.go"),
)},
&filter{
id: "RCNB",
label: "starting reconnect",
desc: "the router to controller control channel connection died and the router trying to reconnect",
LogMatcher: AndMatchers(
FieldContains("msg", "starting reconnection process"),
FieldContains("file", "channel2/reconnecting_impl.go"),
)},
&filter{
id: "RCNF",
label: "reconnect failed",
desc: "the router attempted to reconnect the control channel and failed",
LogMatcher: AndMatchers(
FieldContains("file", "channel2/reconnecting_dialer.go"),
FieldMatches("msg", "reconnection attempt.*failed"),
)},
&filter{
id: "RCNS",
label: "reconnect succeeded",
desc: "the router attempted to reconnect the control channel and succeeded",
LogMatcher: AndMatchers(
FieldEquals("msg", "reconnected"),
FieldContains("file", "channel2/reconnecting_impl.go"),
)},
&filter{
id: "RCNP",
label: "reconnect ping",
desc: "the router is checking the control channel to see if it needs to be reconnected",
LogMatcher: AndMatchers(
FieldContains("file", "channel2/reconnecting_impl.go"),
FieldContains("func", "pingInstance"),
)},
&filter{
id: "RCPF",
label: "reconnect ping failed",
desc: "the router control channel ping failed",
LogMatcher: AndMatchers(
FieldContains("msg", "unable to ping"),
FieldContains("file", "channel2/reconnecting_dialer.go"),
)},
&filter{
id: "HELO",
label: "received ctrl hello",
desc: "the controller sent us a hello message after a control channel was established or reconnected",
LogMatcher: AndMatchers(
FieldContains("msg", "received server hello"),
FieldContains("file", "handler_edge_ctrl/hello.go"),
)},
&filter{
id: "SSST",
label: "api session sync started",
desc: "The controller has started a full api session sync",
LogMatcher: AndMatchers(
FieldContains("file", "handler_edge_ctrl/apiSessionAdded.go"),
FieldMatches("msg", "api session.*starting"),
)},
&filter{
id: "SSCH",
label: "api session data received",
desc: "The controller sent a chunk of api session data",
LogMatcher: AndMatchers(
FieldContains("file", "handler_edge_ctrl/apiSessionAdded.go"),
FieldStartsWith("msg", "received api session sync chunk"),
)},
&filter{
id: "SSDN",
label: "api session sync finished",
desc: "The controller has finished a full api session sync",
LogMatcher: AndMatchers(
FieldContains("file", "handler_edge_ctrl/apiSessionAdded.go"),
FieldStartsWith("msg", "finished sychronizing api sessions"),
)},
&filter{
id: "CHAC",
label: "accepted connection",
desc: "a tcp connection has been accepted",
LogMatcher: AndMatchers(
FieldEquals("msg", "accepted connection"),
FieldContains("file", "transport/tcp/listener.go"),
)},
}
}
func (self *ParseRouterLogs) run(cmd *cobra.Command, args []string) error {
if err := self.validate(); err != nil {
return err
}
ctx := &JsonParseContext{
ParseContext: ParseContext{
path: args[0],
},
}
return ScanJsonLines(ctx, self.examineLogEntry)
}
-78
View File
@@ -1,78 +0,0 @@
//go:build all
/*
Copyright NetFoundry, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"fmt"
"github.com/michaelquigley/pfxlog"
"github.com/openziti/fabric/build"
"github.com/openziti/ziti/common/version"
"github.com/openziti/ziti/ziti-ops/logs"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
var verbose bool
var logFormatter string
func init() {
options := pfxlog.DefaultOptions().SetTrimPrefix("github.com/openziti/").NoColor()
pfxlog.GlobalInit(logrus.InfoLevel, options)
build.InitBuildInfo(version.GetCmdBuildInfo())
root.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose logging")
root.PersistentFlags().StringVar(&logFormatter, "log-formatter", "", "Specify log formatter [json|pfxlog|text]")
root.AddCommand(&cobra.Command{
Use: "version",
Short: "Show component version",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(version.GetBuildMetadata(verbose))
},
})
root.AddCommand(logs.NewLogsCommand())
}
var root = &cobra.Command{
Use: "ziti-ops",
Short: "Ziti Ops Tools",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
if verbose {
logrus.SetLevel(logrus.DebugLevel)
}
switch logFormatter {
case "pfxlog":
pfxlog.SetFormatter(pfxlog.NewFormatter(pfxlog.DefaultOptions().SetTrimPrefix("github.com/openziti/").StartingToday()))
case "json":
pfxlog.SetFormatter(&logrus.JSONFormatter{TimestampFormat: "2006-01-02T15:04:05.000Z"})
case "text":
pfxlog.SetFormatter(&logrus.TextFormatter{})
default:
// let logrus do its own thing
}
},
}
func main() {
if err := root.Execute(); err != nil {
fmt.Printf("error: %s\n", err)
}
}
+69
View File
@@ -0,0 +1,69 @@
package fabric
import (
"context"
"fmt"
"github.com/openziti/fabric/rest_client/inspect"
"github.com/openziti/fabric/rest_model"
"github.com/openziti/foundation/util/stringz"
"github.com/openziti/ziti/ziti/cmd/ziti/cmd/api"
"github.com/openziti/ziti/ziti/cmd/ziti/cmd/common"
"github.com/openziti/ziti/ziti/cmd/ziti/util"
"github.com/spf13/cobra"
)
// newListCmd creates a command object for the "controller list" command
func newInspectCmd(p common.OptionsProvider) *cobra.Command {
listCmd := &ListCmd{Options: api.Options{CommonOptions: p()}}
return listCmd.newCobraCmd()
}
type ListCmd struct {
api.Options
}
func (self *ListCmd) newCobraCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "inspect",
Short: "Inspect runtime application values",
RunE: self.run,
Args: cobra.MinimumNArgs(2),
}
self.AddCommonFlags(cmd)
return cmd
}
func (self *ListCmd) run(cmd *cobra.Command, args []string) error {
client, err := util.NewFabricManagementClient(self)
if err != nil {
return err
}
inspectOk, err := client.Inspect.Inspect(&inspect.InspectParams{
Request: &rest_model.InspectRequest{
AppRegex: &args[0],
RequestedValues: args[1:],
},
Context: context.Background(),
})
if err != nil {
return err
}
result := inspectOk.Payload
if *result.Success {
fmt.Printf("\nResults: (%d)\n", len(result.Values))
for _, value := range result.Values {
fmt.Printf("%v.%v\n", stringz.OrEmpty(value.AppID), stringz.OrEmpty(value.Name))
fmt.Printf("%v\n\n", stringz.OrEmpty(value.Value))
}
} else {
fmt.Printf("\nEncountered errors: (%d)\n", len(result.Errors))
for _, err := range result.Errors {
fmt.Printf("\t%v\n", err)
}
}
return nil
}
+1 -1
View File
@@ -32,7 +32,7 @@ func NewFabricCmd(p common.OptionsProvider) *cobra.Command {
fabricCmd.AddCommand(newAddIdentityCmd(p), newRemoveIdentityCmd(p))
fabricCmd.AddCommand(newCreateCommand(p), newListCmd(p), newUpdateCommand(p), newDeleteCmd(p))
fabricCmd.AddCommand(newInspectCmd(p))
return fabricCmd
}
+147 -78
View File
@@ -7,6 +7,7 @@ import (
"fmt"
httptransport "github.com/go-openapi/runtime/client"
"github.com/openziti/edge/rest_management_api_client"
fabric_rest_client "github.com/openziti/fabric/rest_client"
"github.com/openziti/foundation/common/constants"
"github.com/openziti/foundation/identity/identity"
"github.com/openziti/ziti/ziti/cmd/ziti/cmd/common"
@@ -46,11 +47,13 @@ func (self *RestClientConfig) GetIdentity() string {
}
type RestClientIdentity interface {
NewTlsClientConfig() (*tls.Config, error)
NewClient(timeout time.Duration, verbose bool) (*resty.Client, error)
NewRequest(client *resty.Client) *resty.Request
IsReadOnly() bool
GetBaseUrlForApi(api API) (string, error)
NewEdgeManagementClient(clientOpts EdgeManagementClientOpts) (*rest_management_api_client.ZitiEdgeManagement, error)
NewEdgeManagementClient(clientOpts ClientOpts) (*rest_management_api_client.ZitiEdgeManagement, error)
NewFabricManagementClient(clientOpts ClientOpts) (*fabric_rest_client.ZitiFabric, error)
}
func NewRequest(restClientIdentity RestClientIdentity, timeoutInSeconds int, verbose bool) (*resty.Request, error) {
@@ -74,6 +77,21 @@ func (self *RestClientEdgeIdentity) IsReadOnly() bool {
return self.ReadOnly
}
func (self *RestClientEdgeIdentity) NewTlsClientConfig() (*tls.Config, error) {
rootCaPool := x509.NewCertPool()
rootPemData, err := ioutil.ReadFile(self.CaCert)
if err != nil {
return nil, errors.Errorf("could not read session certificates [%s]: %v", self.CaCert, err)
}
rootCaPool.AppendCertsFromPEM(rootPemData)
return &tls.Config{
RootCAs: rootCaPool,
}, nil
}
func (self *RestClientEdgeIdentity) NewClient(timeout time.Duration, verbose bool) (*resty.Client, error) {
client := newClient()
client.SetRootCertificate(self.CaCert)
@@ -102,85 +120,12 @@ func (self *RestClientEdgeIdentity) GetBaseUrlForApi(api API) (string, error) {
return "", errors.Errorf("unsupport api %v", api)
}
func (self *RestClientEdgeIdentity) NewEdgeManagementClient(clientOpts EdgeManagementClientOpts) (*rest_management_api_client.ZitiEdgeManagement, error) {
respFunc := func(resp *http.Response, err error) {
if clientOpts.OutputResponseJson() {
if resp == nil || resp.Body == nil {
_, _ = fmt.Fprint(clientOpts.OutputWriter(), "<empty response body>\n")
return
}
resp.Body = ioutil.NopCloser(resp.Body)
bodyContent, err := ioutil.ReadAll(resp.Body)
if err != nil {
_, _ = fmt.Fprintf(clientOpts.ErrOutputWriter(), "could not read response body: %v", err)
return
}
bodyStr := string(bodyContent)
_, _ = fmt.Fprint(clientOpts.OutputWriter(), bodyStr, "\n")
}
}
reqFunc := func(request *http.Request) error {
if self.ReadOnly && !strings.EqualFold(request.Method, "get") {
return errors.New("this login is marked read-only, only GET operations are allowed")
}
if clientOpts.OutputRequestJson() {
if request == nil || request.Body == nil {
_, _ = fmt.Fprint(clientOpts.OutputWriter(), "<empty request body>\n")
return nil
}
body, err := request.GetBody()
if err == nil {
_, _ = fmt.Fprintf(clientOpts.ErrOutputWriter(), "could not copy request body: %v", err)
return nil
}
bodyContent, err := ioutil.ReadAll(body)
if err != nil {
bodyStr := string(bodyContent)
_, _ = fmt.Fprint(clientOpts.OutputWriter(), bodyStr, "\n")
return nil
}
}
return nil
}
httpClientTransport := &edgeTransport{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 10 * time.Second,
KeepAlive: 10 * time.Second,
}).DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 10,
IdleConnTimeout: 10 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
},
ResponseFunc: respFunc,
RequestFunc: reqFunc,
}
rootCaPool := x509.NewCertPool()
rootPemData, err := ioutil.ReadFile(self.CaCert)
func (self *RestClientEdgeIdentity) NewEdgeManagementClient(clientOpts ClientOpts) (*rest_management_api_client.ZitiEdgeManagement, error) {
httpClient, err := newRestClientTransport(clientOpts, self)
if err != nil {
return nil, errors.Errorf("could not read session certificates [%s]: %v", self.CaCert, err)
return nil, err
}
rootCaPool.AppendCertsFromPEM(rootPemData)
httpClientTransport.TLSClientConfig = &tls.Config{
RootCAs: rootCaPool,
}
httpClient := &http.Client{
Transport: httpClientTransport,
Timeout: 10 * time.Second,
}
parsedHost, err := url.Parse(self.Url)
if err != nil {
return nil, err
@@ -195,6 +140,26 @@ func (self *RestClientEdgeIdentity) NewEdgeManagementClient(clientOpts EdgeManag
return rest_management_api_client.New(clientRuntime, nil), nil
}
func (self *RestClientEdgeIdentity) NewFabricManagementClient(clientOpts ClientOpts) (*fabric_rest_client.ZitiFabric, error) {
httpClient, err := newRestClientTransport(clientOpts, self)
if err != nil {
return nil, err
}
parsedHost, err := url.Parse(self.Url)
if err != nil {
return nil, err
}
clientRuntime := httptransport.NewWithClient(parsedHost.Host, fabric_rest_client.DefaultBasePath, fabric_rest_client.DefaultSchemes, httpClient)
clientRuntime.DefaultAuthentication = &EdgeManagementAuth{
Token: self.Token,
}
return fabric_rest_client.New(clientRuntime, nil), nil
}
type RestClientFabricIdentity struct {
Url string `json:"url"`
CaCert string `json:"caCert,omitempty"`
@@ -203,6 +168,14 @@ type RestClientFabricIdentity struct {
ReadOnly bool `json:"readOnly"`
}
func (self *RestClientFabricIdentity) NewTlsClientConfig() (*tls.Config, error) {
id, err := identity.LoadClientIdentity(self.ClientCert, self.ClientKey, self.CaCert)
if err != nil {
return nil, errors.Wrap(err, "unable to load identity")
}
return id.ClientTLSConfig(), nil
}
func (self *RestClientFabricIdentity) NewClient(timeout time.Duration, verbose bool) (*resty.Client, error) {
id, err := identity.LoadClientIdentity(self.ClientCert, self.ClientKey, self.CaCert)
if err != nil {
@@ -234,10 +207,26 @@ func (self *RestClientFabricIdentity) IsReadOnly() bool {
return self.ReadOnly
}
func (self *RestClientFabricIdentity) NewEdgeManagementClient(EdgeManagementClientOpts) (*rest_management_api_client.ZitiEdgeManagement, error) {
func (self *RestClientFabricIdentity) NewEdgeManagementClient(ClientOpts) (*rest_management_api_client.ZitiEdgeManagement, error) {
return nil, errors.New("fabric identities cannot be used to connect to the edge management API")
}
func (self *RestClientFabricIdentity) NewFabricManagementClient(clientOpts ClientOpts) (*fabric_rest_client.ZitiFabric, error) {
httpClient, err := newRestClientTransport(clientOpts, self)
if err != nil {
return nil, err
}
parsedHost, err := url.Parse(self.Url)
if err != nil {
return nil, err
}
clientRuntime := httptransport.NewWithClient(parsedHost.Host, fabric_rest_client.DefaultBasePath, fabric_rest_client.DefaultSchemes, httpClient)
return fabric_rest_client.New(clientRuntime, nil), nil
}
func LoadRestClientConfig() (*RestClientConfig, string, error) {
config := &RestClientConfig{}
@@ -370,3 +359,83 @@ func LoadSelectedRWIdentityForApi(api API) (RestClientIdentity, error) {
}
return id, nil
}
func newRestClientResponseF(clientOpts ClientOpts) func(*http.Response, error) {
return func(resp *http.Response, err error) {
if clientOpts.OutputResponseJson() {
if resp == nil || resp.Body == nil {
_, _ = fmt.Fprint(clientOpts.OutputWriter(), "<empty response body>\n")
return
}
resp.Body = ioutil.NopCloser(resp.Body)
bodyContent, err := ioutil.ReadAll(resp.Body)
if err != nil {
_, _ = fmt.Fprintf(clientOpts.ErrOutputWriter(), "could not read response body: %v", err)
return
}
bodyStr := string(bodyContent)
_, _ = fmt.Fprint(clientOpts.OutputWriter(), bodyStr, "\n")
}
}
}
func newRestClientRequestF(clientOpts ClientOpts, readOnly bool) func(*http.Request) error {
return func(request *http.Request) error {
if readOnly && !strings.EqualFold(request.Method, "get") {
return errors.New("this login is marked read-only, only GET operations are allowed")
}
if clientOpts.OutputRequestJson() {
if request == nil || request.Body == nil {
_, _ = fmt.Fprint(clientOpts.OutputWriter(), "<empty request body>\n")
return nil
}
body, err := request.GetBody()
if err == nil {
_, _ = fmt.Fprintf(clientOpts.ErrOutputWriter(), "could not copy request body: %v", err)
return nil
}
bodyContent, err := ioutil.ReadAll(body)
if err != nil {
bodyStr := string(bodyContent)
_, _ = fmt.Fprint(clientOpts.OutputWriter(), bodyStr, "\n")
return nil
}
}
return nil
}
}
func newRestClientTransport(clientOpts ClientOpts, clientIdentity RestClientIdentity) (*http.Client, error) {
httpClientTransport := &edgeTransport{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 10 * time.Second,
KeepAlive: 10 * time.Second,
}).DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 10,
IdleConnTimeout: 10 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
},
ResponseFunc: newRestClientResponseF(clientOpts),
RequestFunc: newRestClientRequestF(clientOpts, clientIdentity.IsReadOnly()),
}
tlsClientConfig, err := clientIdentity.NewTlsClientConfig()
if err != nil {
return nil, err
}
httpClientTransport.TLSClientConfig = tlsClientConfig
httpClient := &http.Client{
Transport: httpClientTransport,
Timeout: 10 * time.Second,
}
return httpClient, nil
}
+11 -2
View File
@@ -29,6 +29,7 @@ import (
"github.com/go-openapi/strfmt"
"github.com/openziti/edge/rest_management_api_client"
"github.com/openziti/edge/rest_model"
fabric_rest_client "github.com/openziti/fabric/rest_client"
"github.com/openziti/ziti/common/version"
cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers"
c "github.com/openziti/ziti/ziti/cmd/ziti/constants"
@@ -766,14 +767,14 @@ func WrapIfApiError(err error) error {
return err
}
type EdgeManagementClientOpts interface {
type ClientOpts interface {
OutputRequestJson() bool
OutputResponseJson() bool
OutputWriter() io.Writer
ErrOutputWriter() io.Writer
}
func NewEdgeManagementClient(clientOpts EdgeManagementClientOpts) (*rest_management_api_client.ZitiEdgeManagement, error) {
func NewEdgeManagementClient(clientOpts ClientOpts) (*rest_management_api_client.ZitiEdgeManagement, error) {
restClientIdentity, err := LoadSelectedIdentity()
if err != nil {
return nil, err
@@ -781,6 +782,14 @@ func NewEdgeManagementClient(clientOpts EdgeManagementClientOpts) (*rest_managem
return restClientIdentity.NewEdgeManagementClient(clientOpts)
}
func NewFabricManagementClient(clientOpts ClientOpts) (*fabric_rest_client.ZitiFabric, error) {
restClientIdentity, err := LoadSelectedIdentity()
if err != nil {
return nil, err
}
return restClientIdentity.NewFabricManagementClient(clientOpts)
}
type EdgeManagementAuth struct {
Token string
}