mirror of
https://github.com/openziti/ziti.git
synced 2026-10-13 07:41:17 +00:00
Ziti probe (#81)
* initial ziti-probe implementation * load config from commandline, fix usage message * fix readme to match code
This commit is contained in:
@@ -21,6 +21,7 @@ require (
|
||||
github.com/golang/protobuf v1.3.5
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/gorilla/mux v1.7.3
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||
github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19
|
||||
github.com/michaelquigley/pfxlog v0.0.0-20190813191113-2be43bd0dccc
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
Ziti Probe
|
||||
----------
|
||||
|
||||
`ziti-probe` is utility application allowing to collect network metrics from the _edge_.
|
||||
|
||||
## Setup
|
||||
|
||||
create config type for metric collection service with the following schema (this is optional)
|
||||
```json
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/ziti-probe-config.v1.json",
|
||||
"type":"object",
|
||||
"additionalProperties": false,
|
||||
"required": [],
|
||||
"properties":{
|
||||
"dbType": {
|
||||
"type":"string"
|
||||
},
|
||||
"dbName": {
|
||||
"type": "string"
|
||||
},
|
||||
"interval": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 3600
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
create service configuration `probe-service-cfg` (exact name does not matter,
|
||||
it wil be used to configure the service in the next step), This is optional
|
||||
```js
|
||||
{
|
||||
"dbType": "influxdb", // only influxdb is currently supported
|
||||
"dbName": "ziti", // name of database in Influxdb, ziti is the default
|
||||
"interval": 60 // how often metrics are uploaded (in seconds), 60 is the default
|
||||
}
|
||||
```
|
||||
create service with a router termination
|
||||
```shell script
|
||||
$ ziti edge controller create service probe-service probe-service-cfg
|
||||
$ ziti edge controller create terminator probe-service router1 tcp:127.0.0.1:8086
|
||||
```
|
||||
in this example `router1` is the name of the fabric router that has access to influxDB server,
|
||||
and `tcp:127.0.0.1:8086` is the address of influxDB server. Adjust as needed.
|
||||
|
||||
create ziti identity(ies) for your probe(s). `ziti-probe` is a Ziti-SDK-enabled application,
|
||||
so standard enrollment procedure should be followed.
|
||||
|
||||
## Running `ziti-probe`
|
||||
|
||||
```shell script
|
||||
$ ziti-probe run <identity-file>
|
||||
```
|
||||
#### What's happening?
|
||||
1. `ziti-probe` authenticates to Ziti controller with provided identity
|
||||
1. it checks that `probe-service` is available and get configuration. If configuration is not assigned default values are used.
|
||||
1. it opens connections to all edge routers configured for `probe-service` (refer to ziti policies)
|
||||
1. each connection performs a round-trip latency probe to each of edge routers every 30 seconds
|
||||
1. `ziti-probe` then submits collected metrics to `probe-service`.
|
||||
metric names are in the following format: `latency.<edgeRouterAddress>`, e.g `latency.tls://54.145.23.14:3022`
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
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 subcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
root.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose logging")
|
||||
root.PersistentFlags().StringVar(&logFormatter, "log-formatter", "", "Specify log formatter [json|pfxlog|text]")
|
||||
}
|
||||
|
||||
var root = &cobra.Command{
|
||||
Use: "ziti-probe",
|
||||
Short: "Ziti Probe",
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
if verbose {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
|
||||
switch logFormatter {
|
||||
case "pfxlog":
|
||||
logrus.SetFormatter(pfxlog.NewFormatterStartingToday())
|
||||
case "json":
|
||||
logrus.SetFormatter(&logrus.JSONFormatter{})
|
||||
case "text":
|
||||
logrus.SetFormatter(&logrus.TextFormatter{})
|
||||
default:
|
||||
// let logrus do its own thing
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
var verbose bool
|
||||
var logFormatter string
|
||||
|
||||
func Execute() {
|
||||
if err := root.Execute(); err != nil {
|
||||
fmt.Printf("error: %s\n", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package subcmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
influxdb "github.com/influxdata/influxdb1-client"
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
"github.com/netfoundry/ziti-foundation/metrics"
|
||||
"github.com/netfoundry/ziti-foundation/metrics/metrics_pb"
|
||||
"github.com/netfoundry/ziti-sdk-golang/ziti"
|
||||
"github.com/netfoundry/ziti-sdk-golang/ziti/config"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var runCmd = &cobra.Command{
|
||||
Use: "run <config>",
|
||||
Short: "Runs ziti probe",
|
||||
Long: "runs ziti probe: captures latency to edge routers and submits them to probe-service",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Run: theProbe.run,
|
||||
}
|
||||
|
||||
func init() {
|
||||
root.AddCommand(runCmd)
|
||||
}
|
||||
|
||||
var log = pfxlog.Logger()
|
||||
|
||||
type probeCfg struct {
|
||||
dbName string
|
||||
dbType string
|
||||
interval int
|
||||
}
|
||||
|
||||
var defaultConfig = probeCfg{
|
||||
dbName: "ziti",
|
||||
dbType: "influxdb",
|
||||
interval: 5 * 60, // 5 minutes
|
||||
}
|
||||
|
||||
type probe struct{
|
||||
cfg *probeCfg
|
||||
indb *influxdb.Client
|
||||
ctx ziti.Context
|
||||
latest atomic.Value
|
||||
closer chan interface{}
|
||||
}
|
||||
|
||||
var theProbe = &probe{
|
||||
cfg: &defaultConfig,
|
||||
closer: make(chan interface{}),
|
||||
}
|
||||
|
||||
func (p *probe) AcceptMetrics(message *metrics_pb.MetricsMessage) {
|
||||
p.latest.Store(message)
|
||||
}
|
||||
|
||||
func (p *probe) sendMetrics() {
|
||||
for {
|
||||
select {
|
||||
case <-time.After(time.Duration(p.cfg.interval) * time.Second):
|
||||
message := p.latest.Load().(*metrics_pb.MetricsMessage)
|
||||
if message == nil {
|
||||
continue
|
||||
}
|
||||
bp, err := metrics.AsBatch(message)
|
||||
if err != nil {
|
||||
logrus.Errorln(err)
|
||||
return
|
||||
}
|
||||
bp.Database = p.cfg.dbName
|
||||
_, err = p.indb.Write(*bp)
|
||||
if err != nil {
|
||||
logrus.Errorln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *probe) run(cmd *cobra.Command, args []string) {
|
||||
|
||||
var err error
|
||||
if len(args) == 0 {
|
||||
log.Infof("loading ziti identity from env.ZITI_SDK_CONFIG[%s]", os.Getenv("ZITI_SDK_CONFIG"))
|
||||
p.ctx = ziti.NewContext()
|
||||
} else {
|
||||
if cfg, err := config.NewFromFile(args[0]); err != nil {
|
||||
log.Fatalf("failed to load config from file[%s]", args[0], err)
|
||||
} else {
|
||||
p.ctx = ziti.NewContextWithConfig(cfg)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_, _ = p.ctx.GetServices()
|
||||
|
||||
_, found := p.ctx.GetService("probe-service")
|
||||
if !found {
|
||||
log.Fatal("required service was not found")
|
||||
}
|
||||
|
||||
p.indb, err = p.createInfluxDbClient()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
_, res, err := p.indb.Ping()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to get server info", err)
|
||||
}
|
||||
log.Info("connected to influx version = ", res)
|
||||
|
||||
p.ctx.Metrics().EventController().AddHandler(p)
|
||||
|
||||
go p.sendMetrics()
|
||||
|
||||
select {
|
||||
case <-p.closer:
|
||||
}
|
||||
|
||||
p.ctx.Close()
|
||||
}
|
||||
|
||||
func (p *probe) createInfluxDbClient() (*influxdb.Client, error) {
|
||||
|
||||
httpC := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: func (c context.Context, network, addr string) (net.Conn, error) {
|
||||
log.Infof("connecting to %s:%s", network, addr)
|
||||
return p.ctx.Dial("probe-service")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
config := influxdb.NewConfig()
|
||||
u, _ := url.Parse("http://probe-service")
|
||||
config.URL = *u
|
||||
clt, _ := influxdb.NewClient(config)
|
||||
|
||||
ic := reflect.ValueOf(clt)
|
||||
ict := ic.Type()
|
||||
hcf, _ := ict.Elem().FieldByName("httpClient")
|
||||
|
||||
hcp := unsafe.Pointer(uintptr(unsafe.Pointer(clt)) + hcf.Offset)
|
||||
|
||||
hcpp := (**http.Client)(hcp)
|
||||
*hcpp = httpC
|
||||
|
||||
return clt, nil
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
"github.com/netfoundry/ziti-cmd/ziti-probe/subcmd"
|
||||
"github.com/netfoundry/ziti-foundation/util/debugz"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func init() {
|
||||
pfxlog.Global(logrus.InfoLevel)
|
||||
pfxlog.SetPrefix("github.com/netfoundry/")
|
||||
}
|
||||
|
||||
func main() {
|
||||
debugz.AddStackDumpHandler()
|
||||
subcmd.Execute()
|
||||
}
|
||||
Reference in New Issue
Block a user