From bc900e29a60467453d9bedc7cae62053aaab72c3 Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Fri, 26 Jul 2024 11:13:40 +0100 Subject: [PATCH 01/21] dont create the extra twilio server if turn off --- adapters/twilio/driver.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/adapters/twilio/driver.go b/adapters/twilio/driver.go index 7b2bac3..0c69d4d 100644 --- a/adapters/twilio/driver.go +++ b/adapters/twilio/driver.go @@ -118,19 +118,21 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { gotTransports[info.Scheme.String()+info.Proto.String()] = true } - //apparently if you go and make a tls turn uri it will work - s := webrtc.ICEServer{ - URLs: []string{"turns:" + tempTurnHost + ":5349?transport=tcp"}, - } + if d.Config.TurnEnabled { + //apparently if you go and make a tls turn uri it will work + s := webrtc.ICEServer{ + URLs: []string{"turns:" + tempTurnHost + ":5349?transport=tcp"}, + } - if responseServers.Username != "" { - s.Username = responseServers.Username - } - if responseServers.Password != "" { - s.Credential = responseServers.Password - } + if responseServers.Username != "" { + s.Username = responseServers.Username + } + if responseServers.Password != "" { + s.Credential = responseServers.Password + } - iceServers = append(iceServers, s) + iceServers = append(iceServers, s) + } return iceServers, nil } From cfdf1a40f49d7e2b89be78f24e1a5775a8965f8b Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Fri, 26 Jul 2024 11:16:07 +0100 Subject: [PATCH 02/21] add in the new api adapter better stats display when not sending to api --- adapters/api/driver.go | 100 +++++++++++++++++++++++++++++++++++++++++ client/client.go | 22 ++++----- client/ice-servers.go | 63 +++++++++++++------------- cmd/iceperf/main.go | 67 +++++++++++++++++++++++---- go.mod | 30 ++++++++----- go.sum | 42 +++++++++++++++++ stats/stats.go | 45 ++++++++++++++++--- 7 files changed, 299 insertions(+), 70 deletions(-) create mode 100644 adapters/api/driver.go diff --git a/adapters/api/driver.go b/adapters/api/driver.go new file mode 100644 index 0000000..a31517e --- /dev/null +++ b/adapters/api/driver.go @@ -0,0 +1,100 @@ +package api + +import ( + "encoding/json" + "errors" + "io" + "net/http" + + "github.com/nimbleape/iceperf-agent/config" + "github.com/pion/webrtc/v4" +) + +type Driver struct { + Config *config.ICEConfig +} + +type ApiIceServer struct { + URL string `json:"url,omitempty"` + Username string `json:"username,omitempty"` + Credential string `json:"credential,omitempty"` +} +type ApiResponse struct { + Providers map[string][]ApiIceServer `json:"providers"` + Location string `json:"location"` +} + +func (d *Driver) GetIceServers() (providersAndIceServers map[string][]webrtc.ICEServer, location string, err error) { + if d.Config.RequestUrl != "" { + + client := &http.Client{} + + req, err := http.NewRequest("POST", d.Config.RequestUrl, nil) + req.Header.Add("Content-Type", "application/json") + req.Header.Add("Authorization", "Bearer "+d.Config.ApiKey) + + if err != nil { + // log.WithFields(log.Fields{ + // "error": err, + // }).Error("Error forming http request") + return nil, "", err + } + + res, err := client.Do(req) + if err != nil { + // log.WithFields(log.Fields{ + // "error": err, + // }).Error("Error doing http response") + return nil, "", err + } + + defer res.Body.Close() + //check the code of the response + if res.StatusCode != 201 { + err = errors.New("error from our api") + // log.WithFields(log.Fields{ + // "code": res.StatusCode, + // "error": err, + // }).Error("Error status code http response") + return nil, "", err + } + + responseData, err := io.ReadAll(res.Body) + if err != nil { + // log.WithFields(log.Fields{ + // "error": err, + // }).Error("Error reading http response") + return nil, "", err + } + // log.Info("got a response back from cloudflare api") + + responseServers := ApiResponse{} + json.Unmarshal([]byte(responseData), &responseServers) + + // log.WithFields(log.Fields{ + // "response": responseServers, + // }).Info("http response") + + location = responseServers.Location + + for k, q := range responseServers.Providers { + iceServers := []webrtc.ICEServer{} + for _, r := range q { + + s := webrtc.ICEServer{ + URLs: []string{r.URL}, + } + + if r.Username != "" { + s.Username = r.Username + } + if r.Credential != "" { + s.Credential = r.Credential + } + iceServers = append(iceServers, s) + } + providersAndIceServers[k] = iceServers + } + } + return +} diff --git a/client/client.go b/client/client.go index 528055d..0529f50 100644 --- a/client/client.go +++ b/client/client.go @@ -40,7 +40,7 @@ type Client struct { close chan struct{} Logger *slog.Logger provider string - stats *stats.Stats + Stats *stats.Stats config *config.Config } @@ -53,13 +53,13 @@ func newClient(cc *config.Config, iceServerInfo *stun.URI, provider string, test // Start timers startTime = time.Now() - stats := stats.NewStats(testRunId.String(), map[string]string{ - "provider": provider, - "scheme": iceServerInfo.Scheme.String(), - "protocol": iceServerInfo.Proto.String(), - "port": fmt.Sprintf("%d", iceServerInfo.Port), - "location": cc.LocationID, - }, testRunStartedAt) + stats := stats.NewStats(testRunId.String(), testRunStartedAt) + + stats.SetProvider(provider) + stats.SetScheme(iceServerInfo.Scheme.String()) + stats.SetProtocol(iceServerInfo.Proto.String()) + stats.SetPort(fmt.Sprintf("%d", iceServerInfo.Port)) + stats.SetLocation(cc.LocationID) connectionPair, err := newConnectionPair(cc, iceServerInfo, provider, stats) @@ -74,7 +74,7 @@ func newClient(cc *config.Config, iceServerInfo *stun.URI, provider string, test close: make(chan struct{}), Logger: cc.Logger, provider: provider, - stats: stats, + Stats: stats, config: cc, } @@ -246,7 +246,7 @@ func (c *Client) Stop() error { if c.config.Logging.API.Enabled { // Convert data to JSON - jsonData, err := json.Marshal(c.stats) + jsonData, err := json.Marshal(c.Stats) if err != nil { fmt.Println("Error marshalling JSON:", err) return err @@ -281,7 +281,7 @@ func (c *Client) Stop() error { fmt.Printf("Failed to send data. Status code: %d\n", resp.StatusCode) } } - j, _ := c.stats.ToJSON() + j, _ := c.Stats.ToJSON() c.Logger.Info(j, "individual_test_completed", "true") return nil diff --git a/client/ice-servers.go b/client/ice-servers.go index 5646a7a..e41a140 100644 --- a/client/ice-servers.go +++ b/client/ice-servers.go @@ -1,6 +1,9 @@ package client import ( + "log/slog" + + "github.com/nimbleape/iceperf-agent/adapters/api" "github.com/nimbleape/iceperf-agent/adapters/cloudflare" "github.com/nimbleape/iceperf-agent/adapters/elixir" "github.com/nimbleape/iceperf-agent/adapters/expressturn" @@ -17,9 +20,18 @@ func formGenericIceServers(config *config.ICEConfig) (iceServers []webrtc.ICESer return nil, nil } -func GetIceServers(config *config.Config) (iceServers map[string][]webrtc.ICEServer, err error) { +func GetIceServers(config *config.Config, logger *slog.Logger) (iceServers map[string][]webrtc.ICEServer, location string, err error) { iceServers = make(map[string][]webrtc.ICEServer) + //check if the API is set and is enabled + if apiConfig, ok := config.ICEConfig["api"]; ok && apiConfig.Enabled { + md := api.Driver{ + Config: &apiConfig, + } + iceServers, location, err = md.GetIceServers() + return + } + //loop through for key, conf := range config.ICEConfig { switch key { @@ -32,10 +44,8 @@ func GetIceServers(config *config.Config) (iceServers map[string][]webrtc.ICESer } is, err := md.GetIceServers() if err != nil { - // log.WithFields(log.Fields{ - // "error": err, - // }).Error("Error getting elixir ice servers") - return nil, err + logger.Error("Error getting elixir ice servers") + return nil, "", err } iceServers[key] = is case "google": @@ -47,10 +57,8 @@ func GetIceServers(config *config.Config) (iceServers map[string][]webrtc.ICESer } is, err := md.GetIceServers() if err != nil { - // log.WithFields(log.Fields{ - // "error": err, - // }).Error("Error getting google ice servers") - return nil, err + logger.Error("Error getting google ice servers") + return nil, "", err } iceServers[key] = is case "metered": @@ -62,10 +70,8 @@ func GetIceServers(config *config.Config) (iceServers map[string][]webrtc.ICESer } is, err := md.GetIceServers() if err != nil { - // log.WithFields(log.Fields{ - // "error": err, - // }).Error("Error getting metered ice servers") - return nil, err + logger.Error("Error getting metered ice servers") + return nil, "", err } iceServers[key] = is case "twilio": @@ -77,10 +83,8 @@ func GetIceServers(config *config.Config) (iceServers map[string][]webrtc.ICESer } is, err := td.GetIceServers() if err != nil { - // log.WithFields(log.Fields{ - // "error": err, - // }).Error("Error getting twilio ice servers") - return nil, err + logger.Error("Error getting twilio ice servers") + return nil, "", err } iceServers[key] = is case "xirsys": @@ -92,10 +96,8 @@ func GetIceServers(config *config.Config) (iceServers map[string][]webrtc.ICESer } is, err := xd.GetIceServers() if err != nil { - // log.WithFields(log.Fields{ - // "error": err, - // }).Error("Error getting xirsys ice servers") - return nil, err + logger.Error("Error getting xirsys ice servers") + return nil, "", err } iceServers[key] = is case "cloudflare": @@ -107,10 +109,8 @@ func GetIceServers(config *config.Config) (iceServers map[string][]webrtc.ICESer } is, err := cd.GetIceServers() if err != nil { - // log.WithFields(log.Fields{ - // "error": err, - // }).Error("Error getting Cloudflare ice servers") - return nil, err + logger.Error("Error getting cloudflare ice servers") + return nil, "", err } iceServers[key] = is case "expressturn": @@ -122,19 +122,16 @@ func GetIceServers(config *config.Config) (iceServers map[string][]webrtc.ICESer } is, err := ed.GetIceServers() if err != nil { - // log.WithFields(log.Fields{ - // "error": err, - // }).Error("Error getting Expressturn ice servers") - return nil, err + logger.Error("Error getting expressturn ice servers") + + return nil, "", err } iceServers[key] = is default: is, err := formGenericIceServers(&conf) if err != nil { - // log.WithFields(log.Fields{ - // "error": err, - // }).Error("Error constructing ice servers") - return nil, err + logger.Error("Error getting generic ice servers") + return nil, "", err } iceServers[key] = is } diff --git a/cmd/iceperf/main.go b/cmd/iceperf/main.go index b9acfde..899b160 100644 --- a/cmd/iceperf/main.go +++ b/cmd/iceperf/main.go @@ -5,12 +5,14 @@ package main import ( "fmt" + "io" "log/slog" "os" "time" "github.com/nimbleape/iceperf-agent/client" "github.com/nimbleape/iceperf-agent/config" + "github.com/nimbleape/iceperf-agent/stats" "github.com/nimbleape/iceperf-agent/version" "github.com/pion/stun/v2" "github.com/pion/webrtc/v4" @@ -27,6 +29,9 @@ import ( // "github.com/grafana/loki-client-go/loki" loki "github.com/magnetde/slog-loki" + + "github.com/fatih/color" + "github.com/rodaine/table" ) func main() { @@ -67,22 +72,45 @@ func runService(ctx *cli.Context) error { var logg *slog.Logger + var loggingLevel slog.Level + + switch config.Logging.Level { + case "debug": + loggingLevel = slog.LevelDebug + case "info": + loggingLevel = slog.LevelInfo + case "error": + loggingLevel = slog.LevelError + default: + loggingLevel = slog.LevelInfo + } + if config.Logging.Loki.Enabled { // config, _ := loki.NewDefaultConfig(config.Logging.Loki.URL) // // config.TenantID = "xyz" // client, _ := loki.New(config) - lokiHandler := loki.NewHandler(config.Logging.Loki.URL, loki.WithLabelsEnabled(loki.LabelAll...)) + lokiHandler := loki.NewHandler( + config.Logging.Loki.URL, + loki.WithLabelsEnabled(loki.LabelAll...), + loki.WithHandler(func(w io.Writer) slog.Handler { + return slog.NewJSONHandler(w, &slog.HandlerOptions{ + Level: loggingLevel, + }) + })) logg = slog.New( slogmulti.Fanout( slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ - Level: slog.LevelInfo, + Level: loggingLevel, }), + // slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{ + // Level: slog.LevelInfo, + // }), lokiHandler, ), - ).With("app", "iceperftest") + ).With("app", "iceperf") // stop loki client and purge buffers defer lokiHandler.Close() @@ -139,11 +167,11 @@ func runService(ctx *cli.Context) error { // log.AddHook(hook) } else { handlerOpts := &slog.HandlerOptions{ - Level: slog.LevelInfo, + Level: loggingLevel, } logg = slog.New(slog.NewTextHandler(os.Stderr, handlerOpts)) } - // slog.SetDefault(logg) + slog.SetDefault(logg) // logg.SetFormatter(&log.JSONFormatter{PrettyPrint: true}) @@ -151,12 +179,16 @@ func runService(ctx *cli.Context) error { // TODO we will make a new client for each ICE Server URL from each provider // get ICE servers and loop them - ICEServers, err := client.GetIceServers(config) + ICEServers, location, err := client.GetIceServers(config, logger) if err != nil { - logger.Error("Error getting ICE servers") + logger.Error("Error getting ICE servers", "err", err) //this should be a fatal } + if location != "" { + config.LocationID = location + } + config.Registry = prometheus.NewRegistry() // pusher := push.New(config.Logging.Loki.URL, "grafanacloud-nimbleape-prom").Gatherer(config.Registry) // pusher := push.New() @@ -188,17 +220,19 @@ func runService(ctx *cli.Context) error { // } // end TEST + var results []*stats.Stats + for provider, iss := range ICEServers { providerLogger := logger.With("Provider", provider) providerLogger.Info("Provider Starting") for _, is := range iss { - iceServerInfo, err := stun.ParseURI(is.URLs[0]) if err != nil { - return err + providerLogger.Error("Error parsing ICE Server URL", "err", err) + continue } runId := xid.New() @@ -238,9 +272,11 @@ func runService(ctx *cli.Context) error { c.Stop() <-time.After(1 * time.Second) iceServerLogger.Info("Finished") + results = append(results, c.Stats) } providerLogger.Info("Provider Finished") } + logger.Info("Finished Test Run") // c, err := client.NewClient(config) @@ -318,6 +354,19 @@ func runService(ctx *cli.Context) error { // logger.Info("Wrote stats to prom") // } + if !config.Logging.Loki.Enabled { + headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc() + columnFmt := color.New(color.FgYellow).SprintfFunc() + + tbl := table.New("Provider", "Scheme", "Time to candidate", "Max Throughput") + tbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt) + + for _, st := range results { + tbl.AddRow(st.Provider, st.Scheme, st.OffererTimeToReceiveCandidate, st.ThroughputMax) + } + + tbl.Print() + } return nil } diff --git a/go.mod b/go.mod index 66e1da3..b3bce50 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/joho/godotenv v1.5.1 github.com/magnetde/slog-loki v0.1.4 github.com/pion/stun/v2 v2.0.0 - github.com/pion/webrtc/v4 v4.0.0-beta.19 + github.com/pion/webrtc/v4 v4.0.0-beta.26 github.com/prometheus/client_golang v1.11.1 github.com/rs/xid v1.5.0 github.com/samber/slog-multi v1.0.3 @@ -23,6 +23,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/fatih/color v1.17.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect @@ -30,32 +31,37 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect - github.com/pion/datachannel v1.5.6 // indirect - github.com/pion/dtls/v2 v2.2.11 // indirect - github.com/pion/ice/v3 v3.0.7 // indirect + github.com/pion/datachannel v1.5.8 // indirect + github.com/pion/dtls/v2 v2.2.12 // indirect + github.com/pion/dtls/v3 v3.0.0 // indirect + github.com/pion/ice/v3 v3.0.13 // indirect github.com/pion/interceptor v0.1.29 // indirect github.com/pion/logging v0.2.2 // indirect github.com/pion/mdns/v2 v2.0.7 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/rtcp v1.2.14 // indirect - github.com/pion/rtp v1.8.6 // indirect - github.com/pion/sctp v1.8.16 // indirect + github.com/pion/rtp v1.8.7 // indirect + github.com/pion/sctp v1.8.19 // indirect github.com/pion/sdp/v3 v3.0.9 // indirect - github.com/pion/srtp/v3 v3.0.1 // indirect - github.com/pion/transport/v2 v2.2.4 // indirect - github.com/pion/transport/v3 v3.0.2 // indirect + github.com/pion/srtp/v3 v3.0.3 // indirect + github.com/pion/transport/v2 v2.2.8 // indirect + github.com/pion/transport/v3 v3.0.6 // indirect github.com/pion/turn/v3 v3.0.3 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.30.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/prometheus/prometheus v1.8.2-0.20210811141203-dcb07e8eac34 // indirect + github.com/rodaine/table v1.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/samber/lo v1.38.1 // indirect + github.com/wlynxg/anet v0.0.3 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - golang.org/x/crypto v0.23.0 // indirect + golang.org/x/crypto v0.25.0 // indirect golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.20.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/sys v0.22.0 // indirect google.golang.org/protobuf v1.34.0 // indirect ) diff --git a/go.sum b/go.sum index 76b5fcf..a67f9f4 100644 --- a/go.sum +++ b/go.sum @@ -386,6 +386,8 @@ github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/foxcpp/go-mockdns v0.0.0-20201212160233-ede2f9158d15/go.mod h1:tPg4cp4nseejPd+UKxtCVQ2hUxNTZ7qQZJa7CLriIeo= @@ -620,6 +622,7 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -841,6 +844,8 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.7/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/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -848,8 +853,12 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= 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.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= @@ -980,11 +989,19 @@ github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pion/datachannel v1.5.6 h1:1IxKJntfSlYkpUj8LlYRSWpYiTTC02nUrOE8T3DqGeg= github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNIVb/NfGW4= +github.com/pion/datachannel v1.5.8 h1:ph1P1NsGkazkjrvyMfhRBUAWMxugJjq2HfQifaOoSNo= +github.com/pion/datachannel v1.5.8/go.mod h1:PgmdpoaNBLX9HNzNClmdki4DYW5JtI7Yibu8QzbL3tI= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks= github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= +github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= +github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= +github.com/pion/dtls/v3 v3.0.0 h1:m2hzwPkzqoBjVKXm5ymNuX01OAjht82TdFL6LoTzgi4= +github.com/pion/dtls/v3 v3.0.0/go.mod h1:tiX7NaneB0wNoRaUpaMVP7igAlkMCTQkbpiY+OfeIi0= github.com/pion/ice/v3 v3.0.7 h1:dfMViRKblENqzorR2cQiiRKWqQfqKZ9+nT/sREX3ra8= github.com/pion/ice/v3 v3.0.7/go.mod h1:pBRcCoJRC0vwvFsemfRIqRLYukV4bPboGb0B4b8AhrQ= +github.com/pion/ice/v3 v3.0.13 h1:tPi5fh2xbWhS0DBcs7LTEG0SOUTHLVDjTlFwBy3hXfw= +github.com/pion/ice/v3 v3.0.13/go.mod h1:q2M/RnfpgGhC4HcluxPpD1wImaqFqU0Z1PE2eeOPrIs= github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M= github.com/pion/interceptor v0.1.29/go.mod h1:ri+LGNjRUc5xUNtDEPzfdkmSqISixVTBF/z/Zms/6T4= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= @@ -999,25 +1016,37 @@ github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9 github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/rtp v1.8.6 h1:MTmn/b0aWWsAzux2AmP8WGllusBVw4NPYPVFFd7jUPw= github.com/pion/rtp v1.8.6/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= +github.com/pion/rtp v1.8.7 h1:qslKkG8qxvQ7hqaxkmL7Pl0XcUm+/Er7nMnu6Vq+ZxM= +github.com/pion/rtp v1.8.7/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/sctp v1.8.13/go.mod h1:YKSgO/bO/6aOMP9LCie1DuD7m+GamiK2yIiPM6vH+GA= github.com/pion/sctp v1.8.16 h1:PKrMs+o9EMLRvFfXq59WFsC+V8mN1wnKzqrv+3D/gYY= github.com/pion/sctp v1.8.16/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE= +github.com/pion/sctp v1.8.19 h1:2CYuw+SQ5vkQ9t0HdOPccsCz1GQMDuVy5PglLgKVBW8= +github.com/pion/sctp v1.8.19/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE= github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY= github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M= github.com/pion/srtp/v3 v3.0.1 h1:AkIQRIZ+3tAOJMQ7G301xtrD1vekQbNeRO7eY1K8ZHk= github.com/pion/srtp/v3 v3.0.1/go.mod h1:3R3a1qIOIxBkVTLGFjafKK6/fJoTdQDhcC67HOyMbJ8= +github.com/pion/srtp/v3 v3.0.3 h1:tRtEOpmR8NtsB/KndlKXFOj/AIIs6aPrCq4TlAatC4M= +github.com/pion/srtp/v3 v3.0.3/go.mod h1:Bp9ztzPCoE0ETca/R+bTVTO5kBgaQMiQkTmZWwazDTc= github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo= github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= +github.com/pion/transport/v2 v2.2.8 h1:HzsqGBChgtF4Cj47gu51l5hONuK/NwgbZL17CMSuwS0= +github.com/pion/transport/v2 v2.2.8/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E= github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4= github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0= +github.com/pion/transport/v3 v3.0.6 h1:k1mQU06bmmX143qSWgXFqSH1KUJceQvIUuVH/K5ELWw= +github.com/pion/transport/v3 v3.0.6/go.mod h1:HvJr2N/JwNJAfipsRleqwFoR3t/pWyHeZUs89v3+t5s= github.com/pion/turn/v3 v3.0.3 h1:1e3GVk8gHZLPBA5LqadWYV60lmaKUaHCkm9DX9CkGcE= github.com/pion/turn/v3 v3.0.3/go.mod h1:vw0Dz420q7VYAF3J4wJKzReLHIo2LGp4ev8nXQexYsc= github.com/pion/webrtc/v4 v4.0.0-beta.19 h1:qAmESbOR4C8Odv+FjGc7qS1sqhWAZJgmSjQFu8pahI8= github.com/pion/webrtc/v4 v4.0.0-beta.19/go.mod h1:yL80J6f59xyNERWAJXFdKZjjQXR4NchgtXr5JDA/2uQ= +github.com/pion/webrtc/v4 v4.0.0-beta.26 h1:umV6Am/1pn6chYAdBT7vleqpuD0cBRD13OxwrpI5O0A= +github.com/pion/webrtc/v4 v4.0.0-beta.26/go.mod h1:nYK4uw3EVTFGrB6LrCuDDEeu/fzsNYokjleAyR4H1v0= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1096,6 +1125,9 @@ github.com/prometheus/prometheus v1.8.2-0.20210811141203-dcb07e8eac34/go.mod h1: github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rodaine/table v1.2.0 h1:38HEnwK4mKSHQJIkavVj+bst1TEY7j9zhLMWu4QJrMA= +github.com/rodaine/table v1.2.0/go.mod h1:wejb/q/Yd4T/SVmBSRMr7GCq3KlcZp3gyNYdLSBhkaE= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -1222,6 +1254,8 @@ github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPyS github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= +github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg= +github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= @@ -1330,6 +1364,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= 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-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1447,6 +1483,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1584,7 +1622,9 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1593,6 +1633,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/stats/stats.go b/stats/stats.go index c8f70e9..486613c 100644 --- a/stats/stats.go +++ b/stats/stats.go @@ -20,26 +20,52 @@ type Stats struct { Throughput map[int64]float64 `json:"throughput"` ThroughputMax float64 `json:"throughputMax"` TestRunStartedAt time.Time `json:"testRunStartedAt"` + Provider string `json:"provider"` + Scheme string `json:"scheme"` + Protocol string `json:"protocol"` + Port string `json:"port"` + Location string `json:"location"` } // NewStats creates a new Stats object with a given test run ID -func NewStats(testRunID string, labels map[string]string, testRunStartedAt time.Time) *Stats { - return &Stats{ +func NewStats(testRunID string, testRunStartedAt time.Time) *Stats { + s := &Stats{ TestRunID: testRunID, TestRunStartedAt: testRunStartedAt, - Labels: labels, Throughput: make(map[int64]float64), // Initialize the Throughput map } + + return s +} + +func (s *Stats) SetProvider(st string) { + s.Provider = st +} + +func (s *Stats) SetScheme(st string) { + s.Scheme = st +} + +func (s *Stats) SetProtocol(st string) { + s.Protocol = st } -func (s *Stats) SetAnswererTimeToReceiveCandidate(c float64) { - s.AnswererTimeToReceiveCandidate = c +func (s *Stats) SetPort(st string) { + s.Port = st +} + +func (s *Stats) SetLocation(st string) { + s.Location = st } func (s *Stats) SetOffererTimeToReceiveCandidate(o float64) { s.OffererTimeToReceiveCandidate = o } +func (s *Stats) SetAnswererTimeToReceiveCandidate(o float64) { + s.AnswererTimeToReceiveCandidate = o +} + func (s *Stats) SetOffererDcBytesSentTotal(d float64) { s.OffererDcBytesSentTotal = d } @@ -79,6 +105,15 @@ func (s *Stats) AddThroughput(tp int64, v float64) { // ToJSON returns the stats object as a JSON string func (s *Stats) ToJSON() (string, error) { + + s.Labels = map[string]string{ + "provider": s.Provider, + "scheme": s.Scheme, + "protocol": s.Protocol, + "port": s.Port, + "location": s.Location, + } + jsonBytes, err := json.Marshal(s) if err != nil { return "", err From 05c9f3998c3be6267d32852aae18ffc92bba403a Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Fri, 26 Jul 2024 11:17:42 +0100 Subject: [PATCH 03/21] dont show the table if youre sending data to api --- cmd/iceperf/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/iceperf/main.go b/cmd/iceperf/main.go index 899b160..6c7a7e0 100644 --- a/cmd/iceperf/main.go +++ b/cmd/iceperf/main.go @@ -354,7 +354,7 @@ func runService(ctx *cli.Context) error { // logger.Info("Wrote stats to prom") // } - if !config.Logging.Loki.Enabled { + if !config.Logging.Loki.Enabled && !config.Logging.API.Enabled { headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc() columnFmt := color.New(color.FgYellow).SprintfFunc() From 9d4ec741df7f6427c089870ee6e93f801386b1a2 Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Fri, 26 Jul 2024 11:20:58 +0100 Subject: [PATCH 04/21] enable sending the bearer token to the api --- client/client.go | 1 + config/config.go | 1 + 2 files changed, 2 insertions(+) diff --git a/client/client.go b/client/client.go index 0529f50..52459c3 100644 --- a/client/client.go +++ b/client/client.go @@ -264,6 +264,7 @@ func (c *Client) Stop() error { // Set the appropriate headers req.Header.Set("Content-Type", "application/json") + req.Header.Add("Authorization", "Bearer "+c.config.Logging.API.ApiKey) // Send the request using the HTTP client client := &http.Client{} diff --git a/config/config.go b/config/config.go index 80687df..11ebddd 100644 --- a/config/config.go +++ b/config/config.go @@ -44,6 +44,7 @@ type PromConfig struct { type ApiConfig struct { Enabled bool `yaml:"enabled"` URI string `yaml:"uri"` + ApiKey string `yaml:"api_key,omitempty"` } type LoggingConfig struct { From 55aed38628b9c0e17983c753009b25895cdb61dd Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Fri, 26 Jul 2024 11:24:35 +0100 Subject: [PATCH 05/21] add latency to the table --- cmd/iceperf/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/iceperf/main.go b/cmd/iceperf/main.go index 6c7a7e0..1015a85 100644 --- a/cmd/iceperf/main.go +++ b/cmd/iceperf/main.go @@ -358,11 +358,11 @@ func runService(ctx *cli.Context) error { headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc() columnFmt := color.New(color.FgYellow).SprintfFunc() - tbl := table.New("Provider", "Scheme", "Time to candidate", "Max Throughput") + tbl := table.New("Provider", "Scheme", "Time to candidate", "Max Throughput", "TURN Transfer Latency") tbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt) for _, st := range results { - tbl.AddRow(st.Provider, st.Scheme, st.OffererTimeToReceiveCandidate, st.ThroughputMax) + tbl.AddRow(st.Provider, st.Scheme, st.OffererTimeToReceiveCandidate, st.ThroughputMax, st.LatencyFirstPacket) } tbl.Print() From a92342c30cc8d2617bd4670f0d709b92103ea4c8 Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Fri, 2 Aug 2024 16:20:07 +0100 Subject: [PATCH 06/21] update to be able to use the api --- adapters/api/driver.go | 55 ++++++++++++++++++++------------- adapters/cloudflare/driver.go | 33 ++++++++++++-------- adapters/elixir/driver.go | 29 +++++++++--------- adapters/expressturn/driver.go | 20 ++++++++---- adapters/google/driver.go | 14 +++++++-- adapters/metered/driver.go | 18 ++++++++--- adapters/twilio/driver.go | 23 +++++++++----- adapters/xirsys/driver.go | 21 ++++++++----- client/client.go | 13 +++++--- client/dataChannel.go | 56 +++++++++++++++++++++++----------- client/ice-servers.go | 47 +++++++++++++++++++++++----- cmd/iceperf/main.go | 21 +++++++++---- config/config.go | 8 ++--- stats/stats.go | 24 ++++++++++----- 14 files changed, 260 insertions(+), 122 deletions(-) diff --git a/adapters/api/driver.go b/adapters/api/driver.go index a31517e..adb8a40 100644 --- a/adapters/api/driver.go +++ b/adapters/api/driver.go @@ -4,32 +4,44 @@ import ( "encoding/json" "errors" "io" + "log/slog" "net/http" + "strings" + "github.com/nimbleape/iceperf-agent/adapters" "github.com/nimbleape/iceperf-agent/config" "github.com/pion/webrtc/v4" + "github.com/rs/xid" ) type Driver struct { Config *config.ICEConfig + Logger *slog.Logger } type ApiIceServer struct { - URL string `json:"url,omitempty"` - Username string `json:"username,omitempty"` - Credential string `json:"credential,omitempty"` + URLs []string `json:"urls,omitempty"` + Username string `json:"username,omitempty"` + Credential string `json:"credential,omitempty"` +} + +type ProviderRes struct { + IceServers []ApiIceServer `json:"iceServers"` + DoThroughput bool `json:"doThroughput"` } type ApiResponse struct { - Providers map[string][]ApiIceServer `json:"providers"` - Location string `json:"location"` + Providers map[string]ProviderRes `json:"providers"` + Node string `json:"node"` } -func (d *Driver) GetIceServers() (providersAndIceServers map[string][]webrtc.ICEServer, location string, err error) { +func (d *Driver) GetIceServers(testRunId xid.ID) (map[string]adapters.IceServersConfig, string, error) { + providersAndIceServers := make(map[string]adapters.IceServersConfig) + if d.Config.RequestUrl != "" { client := &http.Client{} - req, err := http.NewRequest("POST", d.Config.RequestUrl, nil) + req, err := http.NewRequest("POST", d.Config.RequestUrl, strings.NewReader(`{"testRunID": "`+testRunId.String()+`"}`)) req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", "Bearer "+d.Config.ApiKey) @@ -37,7 +49,7 @@ func (d *Driver) GetIceServers() (providersAndIceServers map[string][]webrtc.ICE // log.WithFields(log.Fields{ // "error": err, // }).Error("Error forming http request") - return nil, "", err + return providersAndIceServers, "", err } res, err := client.Do(req) @@ -45,18 +57,14 @@ func (d *Driver) GetIceServers() (providersAndIceServers map[string][]webrtc.ICE // log.WithFields(log.Fields{ // "error": err, // }).Error("Error doing http response") - return nil, "", err + return providersAndIceServers, "", err } defer res.Body.Close() //check the code of the response - if res.StatusCode != 201 { + if res.StatusCode != 200 { err = errors.New("error from our api") - // log.WithFields(log.Fields{ - // "code": res.StatusCode, - // "error": err, - // }).Error("Error status code http response") - return nil, "", err + return providersAndIceServers, "", err } responseData, err := io.ReadAll(res.Body) @@ -64,7 +72,7 @@ func (d *Driver) GetIceServers() (providersAndIceServers map[string][]webrtc.ICE // log.WithFields(log.Fields{ // "error": err, // }).Error("Error reading http response") - return nil, "", err + return providersAndIceServers, "", err } // log.Info("got a response back from cloudflare api") @@ -75,14 +83,15 @@ func (d *Driver) GetIceServers() (providersAndIceServers map[string][]webrtc.ICE // "response": responseServers, // }).Info("http response") - location = responseServers.Location + node := responseServers.Node for k, q := range responseServers.Providers { + iceServers := []webrtc.ICEServer{} - for _, r := range q { + for _, r := range q.IceServers { s := webrtc.ICEServer{ - URLs: []string{r.URL}, + URLs: r.URLs, } if r.Username != "" { @@ -93,8 +102,12 @@ func (d *Driver) GetIceServers() (providersAndIceServers map[string][]webrtc.ICE } iceServers = append(iceServers, s) } - providersAndIceServers[k] = iceServers + providersAndIceServers[k] = adapters.IceServersConfig{ + DoThroughput: q.DoThroughput, + IceServers: iceServers, + } } + return providersAndIceServers, node, nil } - return + return providersAndIceServers, "", nil } diff --git a/adapters/cloudflare/driver.go b/adapters/cloudflare/driver.go index b059d13..a195a1e 100644 --- a/adapters/cloudflare/driver.go +++ b/adapters/cloudflare/driver.go @@ -5,9 +5,11 @@ import ( "errors" "fmt" "io" + "log/slog" "net/http" "strings" + "github.com/nimbleape/iceperf-agent/adapters" "github.com/nimbleape/iceperf-agent/config" "github.com/pion/stun/v2" "github.com/pion/webrtc/v4" @@ -15,6 +17,7 @@ import ( type Driver struct { Config *config.ICEConfig + Logger *slog.Logger } type CloudflareIceServers struct { @@ -22,11 +25,17 @@ type CloudflareIceServers struct { Username string `json:"username,omitempty"` Credential string `json:"credential,omitempty"` } + type CloudflareResponse struct { IceServers CloudflareIceServers `json:"iceServers"` } -func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { +func (d *Driver) GetIceServers() (adapters.IceServersConfig, error) { + + iceServers := adapters.IceServersConfig{ + IceServers: []webrtc.ICEServer{}, + } + if d.Config.RequestUrl != "" { client := &http.Client{} @@ -39,7 +48,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { // log.WithFields(log.Fields{ // "error": err, // }).Error("Error forming http request") - return nil, err + return iceServers, err } res, err := client.Do(req) @@ -47,7 +56,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { // log.WithFields(log.Fields{ // "error": err, // }).Error("Error doing http response") - return nil, err + return iceServers, err } defer res.Body.Close() @@ -58,7 +67,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { // "code": res.StatusCode, // "error": err, // }).Error("Error status code http response") - return nil, err + return iceServers, err } responseData, err := io.ReadAll(res.Body) @@ -66,7 +75,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { // log.WithFields(log.Fields{ // "error": err, // }).Error("Error reading http response") - return nil, err + return iceServers, err } // log.Info("got a response back from cloudflare api") @@ -82,7 +91,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { info, err := stun.ParseURI(r) if err != nil { - return nil, err + return iceServers, err } if ((info.Scheme == stun.SchemeTypeTURN || info.Scheme == stun.SchemeTypeTURNS) && !d.Config.TurnEnabled) || ((info.Scheme == stun.SchemeTypeSTUN || info.Scheme == stun.SchemeTypeSTUNS) && !d.Config.StunEnabled) { @@ -99,13 +108,13 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { if responseServers.IceServers.Credential != "" { s.Credential = responseServers.IceServers.Credential } - iceServers = append(iceServers, s) + iceServers.IceServers = append(iceServers.IceServers, s) } } else { if d.Config.StunHost != "" && d.Config.StunEnabled { if _, ok := d.Config.StunPorts["udp"]; ok { for _, port := range d.Config.StunPorts["udp"] { - iceServers = append(iceServers, webrtc.ICEServer{ + iceServers.IceServers = append(iceServers.IceServers, webrtc.ICEServer{ URLs: []string{fmt.Sprintf("stun:%s:%d", d.Config.StunHost, port)}, }) } @@ -117,7 +126,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { switch transport { case "udp": for _, port := range d.Config.TurnPorts["udp"] { - iceServers = append(iceServers, webrtc.ICEServer{ + iceServers.IceServers = append(iceServers.IceServers, webrtc.ICEServer{ URLs: []string{fmt.Sprintf("turn:%s:%d?transport=udp", d.Config.TurnHost, port)}, Username: d.Config.Username, Credential: d.Config.Password, @@ -125,7 +134,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { } case "tcp": for _, port := range d.Config.TurnPorts["tcp"] { - iceServers = append(iceServers, webrtc.ICEServer{ + iceServers.IceServers = append(iceServers.IceServers, webrtc.ICEServer{ URLs: []string{fmt.Sprintf("turn:%s:%d?transport=tcp", d.Config.TurnHost, port)}, Username: d.Config.Username, Credential: d.Config.Password, @@ -133,7 +142,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { } case "tls": for _, port := range d.Config.TurnPorts["tls"] { - iceServers = append(iceServers, webrtc.ICEServer{ + iceServers.IceServers = append(iceServers.IceServers, webrtc.ICEServer{ URLs: []string{fmt.Sprintf("turns:%s:%d?transport=tcp", d.Config.TurnHost, port)}, Username: d.Config.Username, Credential: d.Config.Password, @@ -144,5 +153,5 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { } } } - return + return iceServers, nil } diff --git a/adapters/elixir/driver.go b/adapters/elixir/driver.go index 73e180b..65f5797 100644 --- a/adapters/elixir/driver.go +++ b/adapters/elixir/driver.go @@ -4,8 +4,10 @@ import ( "encoding/json" "errors" "io" + "log/slog" "net/http" + "github.com/nimbleape/iceperf-agent/adapters" "github.com/nimbleape/iceperf-agent/config" "github.com/pion/stun/v2" "github.com/pion/webrtc/v4" @@ -13,6 +15,7 @@ import ( type Driver struct { Config *config.ICEConfig + Logger *slog.Logger } type ElixirResponse struct { @@ -26,22 +29,20 @@ type ElixirResponse struct { // return nil // } -func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { - client := &http.Client{} +func (d *Driver) GetIceServers() (adapters.IceServersConfig, error) { - if err != nil { - // log.WithFields(log.Fields{ - // "error": err, - // }).Error("Error forming http request URL") - return nil, err + iceServers := adapters.IceServersConfig{ + IceServers: []webrtc.ICEServer{}, } + + client := &http.Client{} req, err := http.NewRequest("POST", d.Config.RequestUrl+"&username="+d.Config.HttpUsername, nil) if err != nil { // log.WithFields(log.Fields{ // "error": err, // }).Error("Error forming http request") - return nil, err + return iceServers, err } res, err := client.Do(req) @@ -49,7 +50,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { // log.WithFields(log.Fields{ // "error": err, // }).Error("Error doing http response") - return nil, err + return iceServers, err } defer res.Body.Close() @@ -59,7 +60,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { // log.WithFields(log.Fields{ // "error": err, // }).Error("Error status code http response") - return nil, err + return iceServers, err } responseData, err := io.ReadAll(res.Body) @@ -67,7 +68,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { // log.WithFields(log.Fields{ // "error": err, // }).Error("Error reading http response") - return nil, err + return iceServers, err } responseServers := ElixirResponse{} @@ -78,7 +79,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { info, err := stun.ParseURI(r) if err != nil { - return nil, err + return iceServers, err } if ((info.Scheme == stun.SchemeTypeTURN || info.Scheme == stun.SchemeTypeTURNS) && !d.Config.TurnEnabled) || ((info.Scheme == stun.SchemeTypeSTUN || info.Scheme == stun.SchemeTypeSTUNS) && !d.Config.StunEnabled) { @@ -96,13 +97,13 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { s.Credential = responseServers.Password } - iceServers = append(iceServers, s) + iceServers.IceServers = append(iceServers.IceServers, s) if d.Config.StunEnabled { stun := webrtc.ICEServer{ URLs: []string{"stun:" + info.Host + ":3478"}, } - iceServers = append(iceServers, stun) + iceServers.IceServers = append(iceServers.IceServers, stun) } } diff --git a/adapters/expressturn/driver.go b/adapters/expressturn/driver.go index e2a3e18..bf68644 100644 --- a/adapters/expressturn/driver.go +++ b/adapters/expressturn/driver.go @@ -2,20 +2,28 @@ package expressturn import ( "fmt" + "log/slog" + "github.com/nimbleape/iceperf-agent/adapters" "github.com/nimbleape/iceperf-agent/config" "github.com/pion/webrtc/v4" ) type Driver struct { Config *config.ICEConfig + Logger *slog.Logger } -func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { +func (d *Driver) GetIceServers() (adapters.IceServersConfig, error) { + + iceServers := adapters.IceServersConfig{ + IceServers: []webrtc.ICEServer{}, + } + if d.Config.StunHost != "" && d.Config.StunEnabled { if _, ok := d.Config.StunPorts["udp"]; ok { for _, port := range d.Config.StunPorts["udp"] { - iceServers = append(iceServers, webrtc.ICEServer{ + iceServers.IceServers = append(iceServers.IceServers, webrtc.ICEServer{ URLs: []string{fmt.Sprintf("stun:%s:%d", d.Config.StunHost, port)}, }) } @@ -27,7 +35,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { switch transport { case "udp": for _, port := range d.Config.TurnPorts["udp"] { - iceServers = append(iceServers, webrtc.ICEServer{ + iceServers.IceServers = append(iceServers.IceServers, webrtc.ICEServer{ URLs: []string{fmt.Sprintf("turn:%s:%d?transport=udp", d.Config.TurnHost, port)}, Username: d.Config.Username, Credential: d.Config.Password, @@ -35,7 +43,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { } case "tcp": for _, port := range d.Config.TurnPorts["tcp"] { - iceServers = append(iceServers, webrtc.ICEServer{ + iceServers.IceServers = append(iceServers.IceServers, webrtc.ICEServer{ URLs: []string{fmt.Sprintf("turn:%s:%d?transport=tcp", d.Config.TurnHost, port)}, Username: d.Config.Username, Credential: d.Config.Password, @@ -43,7 +51,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { } case "tls": for _, port := range d.Config.TurnPorts["tls"] { - iceServers = append(iceServers, webrtc.ICEServer{ + iceServers.IceServers = append(iceServers.IceServers, webrtc.ICEServer{ URLs: []string{fmt.Sprintf("turns:%s:%d?transport=tcp", d.Config.TurnHost, port)}, Username: d.Config.Username, Credential: d.Config.Password, @@ -53,5 +61,5 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { } } } - return + return iceServers, nil } diff --git a/adapters/google/driver.go b/adapters/google/driver.go index 4a5ba4c..d912cc4 100644 --- a/adapters/google/driver.go +++ b/adapters/google/driver.go @@ -2,24 +2,32 @@ package google import ( "fmt" + "log/slog" + "github.com/nimbleape/iceperf-agent/adapters" "github.com/nimbleape/iceperf-agent/config" "github.com/pion/webrtc/v4" ) type Driver struct { Config *config.ICEConfig + Logger *slog.Logger } -func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { +func (d *Driver) GetIceServers() (adapters.IceServersConfig, error) { + + iceServers := adapters.IceServersConfig{ + IceServers: []webrtc.ICEServer{}, + } + if d.Config.StunHost != "" && d.Config.StunEnabled { if _, ok := d.Config.StunPorts["udp"]; ok { for _, port := range d.Config.StunPorts["udp"] { - iceServers = append(iceServers, webrtc.ICEServer{ + iceServers.IceServers = append(iceServers.IceServers, webrtc.ICEServer{ URLs: []string{fmt.Sprintf("stun:%s:%d", d.Config.StunHost, port)}, }) } } } - return + return iceServers, nil } diff --git a/adapters/metered/driver.go b/adapters/metered/driver.go index ec81c48..40ba75c 100644 --- a/adapters/metered/driver.go +++ b/adapters/metered/driver.go @@ -3,8 +3,10 @@ package metered import ( "encoding/json" "io" + "log/slog" "net/http" + "github.com/nimbleape/iceperf-agent/adapters" "github.com/nimbleape/iceperf-agent/config" "github.com/pion/stun/v2" "github.com/pion/webrtc/v4" @@ -12,6 +14,7 @@ import ( type Driver struct { Config *config.ICEConfig + Logger *slog.Logger } type MeteredIceServers struct { @@ -24,13 +27,18 @@ type MeteredIceServers struct { // return nil // } -func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { +func (d *Driver) GetIceServers() (adapters.IceServersConfig, error) { + + iceServers := adapters.IceServersConfig{ + IceServers: []webrtc.ICEServer{}, + } + res, err := http.Get(d.Config.RequestUrl + "?apiKey=" + d.Config.ApiKey) if err != nil { // log.WithFields(log.Fields{ // "error": err, // }).Error("Error making http request") - return nil, err + return iceServers, err } responseData, err := io.ReadAll(res.Body) @@ -38,7 +46,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { // log.WithFields(log.Fields{ // "error": err, // }).Error("Error reading http response") - return nil, err + return iceServers, err } var responseServers []MeteredIceServers @@ -57,7 +65,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { info, err := stun.ParseURI(r.URLs) if err != nil { - return nil, err + return iceServers, err } if ((info.Scheme == stun.SchemeTypeTURN || info.Scheme == stun.SchemeTypeTURNS) && !d.Config.TurnEnabled) || ((info.Scheme == stun.SchemeTypeSTUN || info.Scheme == stun.SchemeTypeSTUNS) && !d.Config.StunEnabled) { @@ -79,7 +87,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { if r.Credential != "" { s.Credential = r.Credential } - iceServers = append(iceServers, s) + iceServers.IceServers = append(iceServers.IceServers, s) gotTransports[info.Scheme.String()+info.Proto.String()] = true } return iceServers, nil diff --git a/adapters/twilio/driver.go b/adapters/twilio/driver.go index 0c69d4d..bb54b25 100644 --- a/adapters/twilio/driver.go +++ b/adapters/twilio/driver.go @@ -4,8 +4,10 @@ import ( "encoding/json" "errors" "io" + "log/slog" "net/http" + "github.com/nimbleape/iceperf-agent/adapters" "github.com/nimbleape/iceperf-agent/config" "github.com/pion/stun/v2" "github.com/pion/webrtc/v4" @@ -14,6 +16,7 @@ import ( type Driver struct { Config *config.ICEConfig + Logger *slog.Logger } type TwilioIceServers struct { @@ -31,9 +34,13 @@ type TwilioResponse struct { IceServers []TwilioIceServers `json:"ice_servers"` } -func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { +func (d *Driver) GetIceServers() (adapters.IceServersConfig, error) { client := &http.Client{} + iceServers := adapters.IceServersConfig{ + IceServers: []webrtc.ICEServer{}, + } + req, err := http.NewRequest("POST", d.Config.RequestUrl, nil) req.SetBasicAuth(d.Config.HttpUsername, d.Config.HttpPassword) @@ -41,7 +48,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { // log.WithFields(log.Fields{ // "error": err, // }).Error("Error forming http request") - return nil, err + return iceServers, err } res, err := client.Do(req) @@ -49,7 +56,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { // log.WithFields(log.Fields{ // "error": err, // }).Error("Error doing http response") - return nil, err + return iceServers, err } defer res.Body.Close() @@ -59,7 +66,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { // log.WithFields(log.Fields{ // "error": err, // }).Error("Error status code http response") - return nil, err + return iceServers, err } responseData, err := io.ReadAll(res.Body) @@ -67,7 +74,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { // log.WithFields(log.Fields{ // "error": err, // }).Error("Error reading http response") - return nil, err + return iceServers, err } responseServers := TwilioResponse{} @@ -88,7 +95,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { info, err := stun.ParseURI(r.URL) if err != nil { - return nil, err + return iceServers, err } if ((info.Scheme == stun.SchemeTypeTURN || info.Scheme == stun.SchemeTypeTURNS) && !d.Config.TurnEnabled) || ((info.Scheme == stun.SchemeTypeSTUN || info.Scheme == stun.SchemeTypeSTUNS) && !d.Config.StunEnabled) { @@ -114,7 +121,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { if r.Credential != "" { s.Credential = r.Credential } - iceServers = append(iceServers, s) + iceServers.IceServers = append(iceServers.IceServers, s) gotTransports[info.Scheme.String()+info.Proto.String()] = true } @@ -131,7 +138,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { s.Credential = responseServers.Password } - iceServers = append(iceServers, s) + iceServers.IceServers = append(iceServers.IceServers, s) } return iceServers, nil diff --git a/adapters/xirsys/driver.go b/adapters/xirsys/driver.go index 15ed662..c9addde 100644 --- a/adapters/xirsys/driver.go +++ b/adapters/xirsys/driver.go @@ -4,9 +4,11 @@ import ( "encoding/json" "errors" "io" + "log/slog" "net/http" "strings" + "github.com/nimbleape/iceperf-agent/adapters" "github.com/nimbleape/iceperf-agent/config" "github.com/pion/stun/v2" "github.com/pion/webrtc/v4" @@ -15,6 +17,7 @@ import ( type Driver struct { Config *config.ICEConfig + Logger *slog.Logger } type XirsysIceServers struct { @@ -31,9 +34,13 @@ type XirsysResponse struct { S string `json:"s"` } -func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { +func (d *Driver) GetIceServers() (adapters.IceServersConfig, error) { client := &http.Client{} + iceServers := adapters.IceServersConfig{ + IceServers: []webrtc.ICEServer{}, + } + req, err := http.NewRequest("PUT", d.Config.RequestUrl, strings.NewReader(`{"format": "urls", "expire": "1800"}`)) req.SetBasicAuth(d.Config.HttpUsername, d.Config.HttpPassword) req.Header.Add("Content-Type", "application/json") @@ -42,7 +49,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { // log.WithFields(log.Fields{ // "error": err, // }).Error("Error forming http request") - return nil, err + return iceServers, err } res, err := client.Do(req) @@ -50,7 +57,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { // log.WithFields(log.Fields{ // "error": err, // }).Error("Error doing http response") - return nil, err + return iceServers, err } defer res.Body.Close() @@ -61,7 +68,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { // "error": err, // "status": res.StatusCode, // }).Error("Error status code http response") - return nil, err + return iceServers, err } responseData, err := io.ReadAll(res.Body) @@ -69,7 +76,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { // log.WithFields(log.Fields{ // "error": err, // }).Error("Error reading http response") - return nil, err + return iceServers, err } responseServers := XirsysResponse{} @@ -88,7 +95,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { info, err := stun.ParseURI(r) if err != nil { - return nil, err + return iceServers, err } if ((info.Scheme == stun.SchemeTypeTURN || info.Scheme == stun.SchemeTypeTURNS) && !d.Config.TurnEnabled) || ((info.Scheme == stun.SchemeTypeSTUN || info.Scheme == stun.SchemeTypeSTUNS) && !d.Config.StunEnabled) { @@ -110,7 +117,7 @@ func (d *Driver) GetIceServers() (iceServers []webrtc.ICEServer, err error) { if responseServers.V.IceServers.Credential != "" { s.Credential = responseServers.V.IceServers.Credential } - iceServers = append(iceServers, s) + iceServers.IceServers = append(iceServers.IceServers, s) gotTransports[info.Scheme.String()+info.Proto.String()] = true } diff --git a/client/client.go b/client/client.go index 52459c3..623bb36 100644 --- a/client/client.go +++ b/client/client.go @@ -44,11 +44,11 @@ type Client struct { config *config.Config } -func NewClient(config *config.Config, iceServerInfo *stun.URI, provider string, testRunId xid.ID, testRunStartedAt time.Time) (c *Client, err error) { - return newClient(config, iceServerInfo, provider, testRunId, testRunStartedAt) +func NewClient(config *config.Config, iceServerInfo *stun.URI, provider string, testRunId xid.ID, testRunStartedAt time.Time, doThroughputTest bool, close chan struct{}) (c *Client, err error) { + return newClient(config, iceServerInfo, provider, testRunId, testRunStartedAt, doThroughputTest, close) } -func newClient(cc *config.Config, iceServerInfo *stun.URI, provider string, testRunId xid.ID, testRunStartedAt time.Time) (*Client, error) { +func newClient(cc *config.Config, iceServerInfo *stun.URI, provider string, testRunId xid.ID, testRunStartedAt time.Time, doThroughputTest bool, close chan struct{}) (*Client, error) { // Start timers startTime = time.Now() @@ -59,9 +59,9 @@ func newClient(cc *config.Config, iceServerInfo *stun.URI, provider string, test stats.SetScheme(iceServerInfo.Scheme.String()) stats.SetProtocol(iceServerInfo.Proto.String()) stats.SetPort(fmt.Sprintf("%d", iceServerInfo.Port)) - stats.SetLocation(cc.LocationID) + stats.SetNode(cc.NodeID) - connectionPair, err := newConnectionPair(cc, iceServerInfo, provider, stats) + connectionPair, err := newConnectionPair(cc, iceServerInfo, provider, stats, doThroughputTest, close) if err != nil { return nil, err @@ -154,12 +154,14 @@ func newClient(cc *config.Config, iceServerInfo *stun.URI, provider string, test // "timeSinceStartMs": time.Since(startTime).Milliseconds(), // }).Info("Offerer Stats") // } + stats.SetTimeToConnectedState(time.Since(startTime).Milliseconds()) c.OffererConnected <- true case webrtc.PeerConnectionStateFailed: // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. c.ConnectionPair.LogOfferer.Error("Offerer connection failed", "eventTime", time.Now(), "timeSinceStartMs", time.Since(startTime).Milliseconds()) + close <- struct{}{} c.OffererConnected <- false case webrtc.PeerConnectionStateClosed: // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify @@ -246,6 +248,7 @@ func (c *Client) Stop() error { if c.config.Logging.API.Enabled { // Convert data to JSON + c.Stats.CreateLabels() jsonData, err := json.Marshal(c.Stats) if err != nil { fmt.Println("Error marshalling JSON:", err) diff --git a/client/dataChannel.go b/client/dataChannel.go index f345685..d925147 100644 --- a/client/dataChannel.go +++ b/client/dataChannel.go @@ -31,24 +31,27 @@ type ConnectionPair struct { iceServerInfo *stun.URI provider string stats *stats.Stats + doThroughputTest bool + closeChan chan struct{} } -func NewConnectionPair(config *config.Config, iceServerInfo *stun.URI, provider string, stats *stats.Stats) (c *ConnectionPair, err error) { - return newConnectionPair(config, iceServerInfo, provider, stats) +func NewConnectionPair(config *config.Config, iceServerInfo *stun.URI, provider string, stats *stats.Stats, doThroughputTest bool, closeChan chan struct{}) (c *ConnectionPair, err error) { + return newConnectionPair(config, iceServerInfo, provider, stats, doThroughputTest, closeChan) } -func newConnectionPair(cc *config.Config, iceServerInfo *stun.URI, provider string, stats *stats.Stats) (*ConnectionPair, error) { - +func newConnectionPair(cc *config.Config, iceServerInfo *stun.URI, provider string, stats *stats.Stats, doThroughputTest bool, closeChan chan struct{}) (*ConnectionPair, error) { logOfferer := cc.Logger.With("peer", "Offerer") logAnswerer := cc.Logger.With("peer", "Answerer") cp := &ConnectionPair{ - config: cc, - LogOfferer: logOfferer, - LogAnswerer: logAnswerer, - iceServerInfo: iceServerInfo, - provider: provider, - stats: stats, + config: cc, + LogOfferer: logOfferer, + LogAnswerer: logAnswerer, + iceServerInfo: iceServerInfo, + provider: provider, + stats: stats, + doThroughputTest: doThroughputTest, + closeChan: closeChan, } config := webrtc.Configuration{} @@ -89,7 +92,11 @@ func (cp *ConnectionPair) setRemoteDescription(pc *webrtc.PeerConnection, sdp [] func (cp *ConnectionPair) createOfferer(config webrtc.Configuration) { // Create a new PeerConnection - pc, err := webrtc.NewPeerConnection(config) + settingEngine := webrtc.SettingEngine{} + settingEngine.SetICETimeouts(5*time.Second, 10*time.Second, 2*time.Second) + api := webrtc.NewAPI(webrtc.WithSettingEngine(settingEngine)) + + pc, err := api.NewPeerConnection(config) util.Check(err) buf := make([]byte, 1024) @@ -158,9 +165,13 @@ func (cp *ConnectionPair) createOfferer(config webrtc.Configuration) { dc.OnBufferedAmountLow(func() { // Make sure to not block this channel or perform long running operations in this callback // This callback is executed by pion/sctp. If this callback is blocking it will stop operations - select { - case sendMoreCh <- struct{}{}: - default: + if cp.doThroughputTest { + select { + case sendMoreCh <- struct{}{}: + default: + } + } else { + //a noop } }) @@ -179,6 +190,10 @@ func (cp *ConnectionPair) createOfferer(config webrtc.Configuration) { } func (cp *ConnectionPair) createAnswerer(config webrtc.Configuration) { + + // settingEngine := webrtc.SettingEngine{} + // settingEngine.SetICETimeouts(5, 5, 2) + // api := webrtc.NewAPI(webrtc.WithSettingEngine(settingEngine)) // Create a new PeerConnection pc, err := webrtc.NewPeerConnection(config) util.Check(err) @@ -215,15 +230,18 @@ func (cp *ConnectionPair) createAnswerer(config webrtc.Configuration) { // bps := float64(atomic.LoadUint64(&totalBytesReceived)*8) / time.Since(since).Seconds() cp.LogAnswerer.Info("On ticker: Calculated throughput", "throughput", bps/1024/1024, "eventTime", time.Now()) - - cp.stats.AddThroughput(time.Since(since).Milliseconds(), bps/1024/1024) + if cp.doThroughputTest { + cp.stats.AddThroughput(time.Since(since).Milliseconds(), bps/1024/1024) + } } bps := 8 * float64(totalBytesReceived) / float64(time.Since(since).Seconds()) // bps := float64(atomic.LoadUint64(&totalBytesReceived)*8) / time.Since(since).Seconds() cp.LogAnswerer.Info("On ticker: Calculated throughput", "throughput", bps/1024/1024, "eventTime", time.Now(), "timeSinceStartMs", time.Since(since).Milliseconds()) - cp.stats.AddThroughput(time.Since(since).Milliseconds(), bps/1024/1024) + if cp.doThroughputTest { + cp.stats.AddThroughput(time.Since(since).Milliseconds(), bps/1024/1024) + } }) // Register the OnMessage to handle incoming messages @@ -240,6 +258,10 @@ func (cp *ConnectionPair) createAnswerer(config webrtc.Configuration) { // cp.LogAnswerer.Info("Received Bytes So Far", "dcReceivedBytes", totalBytesReceivedTmp, // "cpReceivedBytes", cpTotalBytesReceivedTmp) } + if !cp.doThroughputTest { + cp.LogAnswerer.Info("Sending to close") + cp.closeChan <- struct{}{} + } }) dc.OnClose(func() { diff --git a/client/ice-servers.go b/client/ice-servers.go index e41a140..ee738b4 100644 --- a/client/ice-servers.go +++ b/client/ice-servers.go @@ -3,6 +3,7 @@ package client import ( "log/slog" + "github.com/nimbleape/iceperf-agent/adapters" "github.com/nimbleape/iceperf-agent/adapters/api" "github.com/nimbleape/iceperf-agent/adapters/cloudflare" "github.com/nimbleape/iceperf-agent/adapters/elixir" @@ -13,40 +14,52 @@ import ( "github.com/nimbleape/iceperf-agent/adapters/xirsys" "github.com/nimbleape/iceperf-agent/config" "github.com/pion/webrtc/v4" + "github.com/rs/xid" // log "github.com/sirupsen/logrus" ) -func formGenericIceServers(config *config.ICEConfig) (iceServers []webrtc.ICEServer, err error) { - return nil, nil +func formGenericIceServers(config *config.ICEConfig) (adapters.IceServersConfig, error) { + return adapters.IceServersConfig{}, nil } -func GetIceServers(config *config.Config, logger *slog.Logger) (iceServers map[string][]webrtc.ICEServer, location string, err error) { - iceServers = make(map[string][]webrtc.ICEServer) +type IceServersConfig struct { + IceServers map[string][]webrtc.ICEServer + DoThroughput bool +} + +func GetIceServers(config *config.Config, logger *slog.Logger, testRunId xid.ID) (map[string]adapters.IceServersConfig, string, error) { //check if the API is set and is enabled if apiConfig, ok := config.ICEConfig["api"]; ok && apiConfig.Enabled { md := api.Driver{ Config: &apiConfig, + Logger: logger, } - iceServers, location, err = md.GetIceServers() - return + iceServers, node, err := md.GetIceServers(testRunId) + return iceServers, node, err } + iceServers := make(map[string]adapters.IceServersConfig) + //loop through for key, conf := range config.ICEConfig { switch key { + case "api": + continue case "elixir": if !conf.Enabled { continue } md := elixir.Driver{ Config: &conf, + Logger: logger, } is, err := md.GetIceServers() if err != nil { logger.Error("Error getting elixir ice servers") return nil, "", err } + logger.Info("elixir IceServers", "is", is) iceServers[key] = is case "google": if !conf.Enabled { @@ -54,12 +67,15 @@ func GetIceServers(config *config.Config, logger *slog.Logger) (iceServers map[s } md := google.Driver{ Config: &conf, + Logger: logger, } is, err := md.GetIceServers() if err != nil { logger.Error("Error getting google ice servers") return nil, "", err } + logger.Info("google IceServers", "is", is) + iceServers[key] = is case "metered": if !conf.Enabled { @@ -67,12 +83,15 @@ func GetIceServers(config *config.Config, logger *slog.Logger) (iceServers map[s } md := metered.Driver{ Config: &conf, + Logger: logger, } is, err := md.GetIceServers() if err != nil { logger.Error("Error getting metered ice servers") return nil, "", err } + logger.Info("metered IceServers", "is", is) + iceServers[key] = is case "twilio": if !conf.Enabled { @@ -80,12 +99,15 @@ func GetIceServers(config *config.Config, logger *slog.Logger) (iceServers map[s } td := twilio.Driver{ Config: &conf, + Logger: logger, } is, err := td.GetIceServers() if err != nil { logger.Error("Error getting twilio ice servers") return nil, "", err } + logger.Info("twilio IceServers", "is", is) + iceServers[key] = is case "xirsys": if !conf.Enabled { @@ -93,12 +115,15 @@ func GetIceServers(config *config.Config, logger *slog.Logger) (iceServers map[s } xd := xirsys.Driver{ Config: &conf, + Logger: logger, } is, err := xd.GetIceServers() if err != nil { logger.Error("Error getting xirsys ice servers") return nil, "", err } + logger.Info("xirsys IceServers", "is", is) + iceServers[key] = is case "cloudflare": if !conf.Enabled { @@ -106,12 +131,15 @@ func GetIceServers(config *config.Config, logger *slog.Logger) (iceServers map[s } cd := cloudflare.Driver{ Config: &conf, + Logger: logger, } is, err := cd.GetIceServers() if err != nil { logger.Error("Error getting cloudflare ice servers") return nil, "", err } + logger.Info("cloudflare IceServers", "is", is) + iceServers[key] = is case "expressturn": if !conf.Enabled { @@ -119,6 +147,7 @@ func GetIceServers(config *config.Config, logger *slog.Logger) (iceServers map[s } ed := expressturn.Driver{ Config: &conf, + Logger: logger, } is, err := ed.GetIceServers() if err != nil { @@ -126,6 +155,8 @@ func GetIceServers(config *config.Config, logger *slog.Logger) (iceServers map[s return nil, "", err } + logger.Info("expressturn IceServers", "is", is) + iceServers[key] = is default: is, err := formGenericIceServers(&conf) @@ -133,9 +164,11 @@ func GetIceServers(config *config.Config, logger *slog.Logger) (iceServers map[s logger.Error("Error getting generic ice servers") return nil, "", err } + logger.Info("default IceServers", "key", key, "is", is) + iceServers[key] = is } } - return + return iceServers, "", nil } diff --git a/cmd/iceperf/main.go b/cmd/iceperf/main.go index 1015a85..77669aa 100644 --- a/cmd/iceperf/main.go +++ b/cmd/iceperf/main.go @@ -179,14 +179,14 @@ func runService(ctx *cli.Context) error { // TODO we will make a new client for each ICE Server URL from each provider // get ICE servers and loop them - ICEServers, location, err := client.GetIceServers(config, logger) + ICEServers, node, err := client.GetIceServers(config, logger, testRunId) if err != nil { logger.Error("Error getting ICE servers", "err", err) //this should be a fatal } - if location != "" { - config.LocationID = location + if node != "" { + config.NodeID = node } config.Registry = prometheus.NewRegistry() @@ -227,7 +227,10 @@ func runService(ctx *cli.Context) error { providerLogger.Info("Provider Starting") - for _, is := range iss { + for _, is := range iss.IceServers { + + providerLogger.Info("URL is", "url", is) + iceServerInfo, err := stun.ParseURI(is.URLs[0]) if err != nil { @@ -259,7 +262,9 @@ func runService(ctx *cli.Context) error { } timer := time.NewTimer(testDuration) - c, err := client.NewClient(config, iceServerInfo, provider, testRunId, testRunStartedAt) + close := make(chan struct{}) + + c, err := client.NewClient(config, iceServerInfo, provider, testRunId, testRunStartedAt, iss.DoThroughput, close) if err != nil { return err } @@ -267,7 +272,11 @@ func runService(ctx *cli.Context) error { iceServerLogger.Info("Calling Run()") c.Run() iceServerLogger.Info("Called Run(), waiting for timer", "seconds", testDuration.Seconds()) - <-timer.C + select { + case <-close: + timer.Stop() + case <-timer.C: + } iceServerLogger.Info("Calling Stop()") c.Stop() <-time.After(1 * time.Second) diff --git a/config/config.go b/config/config.go index 11ebddd..04bb947 100644 --- a/config/config.go +++ b/config/config.go @@ -23,6 +23,7 @@ type ICEConfig struct { StunPorts map[string][]int `yaml:"stun_ports,omitempty"` StunEnabled bool `yaml:"stun_enabled"` TurnEnabled bool `yaml:"turn_enabled"` + DoThroughput bool `yaml:"do_throughput"` } type LokiConfig struct { @@ -55,9 +56,9 @@ type LoggingConfig struct { } type Config struct { - LocationID string `yaml:"location_id"` - ICEConfig map[string]ICEConfig `yaml:"ice_servers"` - Logging LoggingConfig `yaml:"logging"` + NodeID string `yaml:"node_id"` + ICEConfig map[string]ICEConfig `yaml:"ice_servers"` + Logging LoggingConfig `yaml:"logging"` WebRTCConfig webrtc.Configuration // TODO the following should be different for answerer and offerer sides @@ -66,7 +67,6 @@ type Config struct { // internal ServiceName string `yaml:"-"` - NodeID string // Do not provide, will be overwritten Logger *slog.Logger Registry *prometheus.Registry } diff --git a/stats/stats.go b/stats/stats.go index 486613c..d597c9f 100644 --- a/stats/stats.go +++ b/stats/stats.go @@ -24,7 +24,9 @@ type Stats struct { Scheme string `json:"scheme"` Protocol string `json:"protocol"` Port string `json:"port"` - Location string `json:"location"` + Node string `json:"node"` + TimeToConnectedState int64 `json:"timeToConnectedState"` + Connected bool `json:"connected"` } // NewStats creates a new Stats object with a given test run ID @@ -33,11 +35,17 @@ func NewStats(testRunID string, testRunStartedAt time.Time) *Stats { TestRunID: testRunID, TestRunStartedAt: testRunStartedAt, Throughput: make(map[int64]float64), // Initialize the Throughput map + Connected: false, } return s } +func (s *Stats) SetTimeToConnectedState(t int64) { + s.TimeToConnectedState = t + s.Connected = true +} + func (s *Stats) SetProvider(st string) { s.Provider = st } @@ -54,8 +62,8 @@ func (s *Stats) SetPort(st string) { s.Port = st } -func (s *Stats) SetLocation(st string) { - s.Location = st +func (s *Stats) SetNode(st string) { + s.Node = st } func (s *Stats) SetOffererTimeToReceiveCandidate(o float64) { @@ -103,18 +111,20 @@ func (s *Stats) AddThroughput(tp int64, v float64) { } } -// ToJSON returns the stats object as a JSON string -func (s *Stats) ToJSON() (string, error) { - +func (s *Stats) CreateLabels() { s.Labels = map[string]string{ "provider": s.Provider, "scheme": s.Scheme, "protocol": s.Protocol, "port": s.Port, - "location": s.Location, + "location": s.Node, } +} +// ToJSON returns the stats object as a JSON string +func (s *Stats) ToJSON() (string, error) { jsonBytes, err := json.Marshal(s) + if err != nil { return "", err } From 8d377a3bb70fc91681f330e05169e0f495dd06b3 Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Fri, 2 Aug 2024 16:27:47 +0100 Subject: [PATCH 07/21] add missing code --- adapters/types.go | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 adapters/types.go diff --git a/adapters/types.go b/adapters/types.go new file mode 100644 index 0000000..76e5947 --- /dev/null +++ b/adapters/types.go @@ -0,0 +1,8 @@ +package adapters + +import "github.com/pion/webrtc/v4" + +type IceServersConfig struct { + IceServers []webrtc.ICEServer + DoThroughput bool +} From 232504ab88a4c248ec5c511ada3ef8ab0d11a397 Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Sat, 3 Aug 2024 10:38:51 +0100 Subject: [PATCH 08/21] fix tests --- specifications/specifications.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specifications/specifications.go b/specifications/specifications.go index 59787ce..63864fa 100644 --- a/specifications/specifications.go +++ b/specifications/specifications.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/alecthomas/assert/v2" - "github.com/pion/webrtc/v4" + "github.com/nimbleape/iceperf-agent/adapters" ) /* @@ -19,7 +19,7 @@ type ServerConnect interface { Connect() (bool, error) } type TURNProvider interface { - GetIceServers() ([]webrtc.ICEServer, error) + GetIceServers() (adapters.IceServersConfig, error) } func ConnectToServerSpecification(t testing.TB, serverConnect ServerConnect) { @@ -31,5 +31,5 @@ func ConnectToServerSpecification(t testing.TB, serverConnect ServerConnect) { func GetIceServersSpecification(t testing.TB, provider TURNProvider) { is, err := provider.GetIceServers() assert.NoError(t, err) - assert.True(t, len(is) > 0) + assert.True(t, len(is.IceServers) > 0) } From d02033cee3f895974a015a2b7d101a6a3e677004 Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Sun, 4 Aug 2024 00:33:34 +0100 Subject: [PATCH 09/21] change build and allow for timed tests --- .github/workflows/go.yml | 51 +++++++++++++++++++++++----------------- cmd/iceperf/main.go | 22 +++++++++++++---- config/config.go | 6 +++++ 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 318d1bb..28d628d 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -7,6 +7,7 @@ on: push: branches: - "main" + - "add-updates" tags: - "v*.*.*" pull_request: @@ -47,34 +48,40 @@ jobs: needs: - test concurrency: - group: ${{ github.workflow }}-${{ matrix.os }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ matrix.goos }}-${{ github.ref }} cancel-in-progress: true + runs-on: ubuntu-latest strategy: matrix: - include: - - os: macos-latest-xlarge - binary-name: iceperf-darwin-arm64 - - os: windows-latest - binary-name: iceperf-windows-x86-64 - - os: ubuntu-latest - binary-name: iceperf-linux-x86-64 - runs-on: ${{ matrix.os }} + # build and publish in parallel: linux/386, linux/amd64, linux/arm64, windows/386, windows/amd64, darwin/amd64, darwin/arm64 + goos: [linux, windows, darwin] + goarch: [amd64, arm64] + exclude: + - goarch: arm64 + goos: windows steps: - uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: '1.22.x' - - - name: Install dependencies - run: go get ./cmd/iceperf + # - name: Set up Go + # uses: actions/setup-go@v4 + # with: + # go-version: '1.22.x' - - name: Build - run: go build -o ${{ matrix.binary-name }} ./cmd/iceperf + # - name: Install dependencies + # run: go get ./cmd/iceperf - - name: Release - uses: softprops/action-gh-release@v2 - if: startsWith(github.ref, 'refs/tags/') + - uses: wangyoucao577/go-release-action@v1 with: - files: ${{ matrix.binary-name }} + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + project_path: "./cmd/iceperf" + + # - name: Build + # run: go build -o ${{ matrix.binary-name }} ./cmd/iceperf + + # - name: Release + # uses: softprops/action-gh-release@v2 + # if: startsWith(github.ref, 'refs/tags/') + # with: + # files: ${{ matrix.binary-name }} diff --git a/cmd/iceperf/main.go b/cmd/iceperf/main.go index 77669aa..74fbbce 100644 --- a/cmd/iceperf/main.go +++ b/cmd/iceperf/main.go @@ -62,9 +62,6 @@ func runService(ctx *cli.Context) error { return err } - testRunId := xid.New() - testRunStartedAt := time.Now() - lvl := new(slog.LevelVar) lvl.Set(slog.LevelInfo) @@ -173,8 +170,26 @@ func runService(ctx *cli.Context) error { } slog.SetDefault(logg) + if config.Timer.Enabled { + ticker := time.NewTicker(time.Duration(config.Timer.Interval) * time.Second) + runTest(logg, config) + for { + <-ticker.C + runTest(logg, config) + } + } else { + runTest(logg, config) + } + + return nil +} + +func runTest(logg *slog.Logger, config *config.Config) error { // logg.SetFormatter(&log.JSONFormatter{PrettyPrint: true}) + testRunId := xid.New() + testRunStartedAt := time.Now() + logger := logg.With("testRunId", testRunId) // TODO we will make a new client for each ICE Server URL from each provider @@ -376,7 +391,6 @@ func runService(ctx *cli.Context) error { tbl.Print() } - return nil } diff --git a/config/config.go b/config/config.go index 04bb947..f7f14b7 100644 --- a/config/config.go +++ b/config/config.go @@ -55,10 +55,16 @@ type LoggingConfig struct { Prometheus PromConfig `yaml:"prometheus"` } +type TimerConfig struct { + Enabled bool `yaml:"enabled"` + Interval int `yaml:"interval"` +} + type Config struct { NodeID string `yaml:"node_id"` ICEConfig map[string]ICEConfig `yaml:"ice_servers"` Logging LoggingConfig `yaml:"logging"` + Timer TimerConfig `yaml:"timer"` WebRTCConfig webrtc.Configuration // TODO the following should be different for answerer and offerer sides From 6f64d1baaa806aa601360107e237c590723f0309 Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Sun, 4 Aug 2024 00:36:57 +0100 Subject: [PATCH 10/21] add in arch --- .github/workflows/go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 28d628d..b4a9da6 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -48,7 +48,7 @@ jobs: needs: - test concurrency: - group: ${{ github.workflow }}-${{ matrix.goos }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ matrix.goos }}-${{ matrix.goarch }}-${{ github.ref }} cancel-in-progress: true runs-on: ubuntu-latest strategy: From bbfab18cfe303db22ca16cfa4a6a5037be4379fa Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Sun, 4 Aug 2024 00:39:56 +0100 Subject: [PATCH 11/21] only build on tag --- .github/workflows/go.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index b4a9da6..2d2c8cd 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -5,9 +5,6 @@ name: Build and Test App on: push: - branches: - - "main" - - "add-updates" tags: - "v*.*.*" pull_request: From 2542d34566edf297f0c5b91a98e117a7c61454c0 Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Sun, 4 Aug 2024 08:53:02 +0100 Subject: [PATCH 12/21] run on tag created --- .github/workflows/go.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 2d2c8cd..f480b58 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -4,9 +4,8 @@ name: Build and Test App on: - push: - tags: - - "v*.*.*" + release: + types: [created] pull_request: branches: [ "main" ] From 771a409259aa7705a7c00fc56ad4a60d11aa2544 Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Sun, 4 Aug 2024 09:09:09 +0100 Subject: [PATCH 13/21] get the tag building --- .github/workflows/go.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index f480b58..1954eb8 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -4,6 +4,9 @@ name: Build and Test App on: + push: + tags: + - "v*.*.*" release: types: [created] pull_request: From d04dd2fc743d7de983d3a699cd105add69643d6f Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Sun, 4 Aug 2024 16:57:21 +0100 Subject: [PATCH 14/21] add dockerfile --- .github/workflows/go.yml | 105 ++++++++++++++++++++++++++++++++++++++- Dockerfile | 25 ++++++++++ 2 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 Dockerfile diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 1954eb8..5b88c60 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -12,6 +12,9 @@ on: pull_request: branches: [ "main" ] +env: + REGISTRY_IMAGE: nimbleape/iceperf-agent + jobs: test: name: Test @@ -42,8 +45,108 @@ jobs: XIRSYS_HTTP_USERNAME: ${{ secrets.XIRSYS_HTTP_USERNAME }} XIRSYS_HTTP_PASSWORD: ${{ secrets.XIRSYS_HTTP_PASSWORD }} XIRSYS_REQUEST_URL: ${{ secrets.XIRSYS_REQUEST_URL }} + build-docker: + name: Build Docker + needs: + - test + concurrency: + group: ${{ github.workflow }}-${{ matrix.platform }}-${{ github.ref }} + cancel-in-progress: true + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + platform: + - linux/amd64 + - linux/arm/v6 + - linux/arm/v7 + - linux/arm64 + - darwin/arm64 + + steps: + - uses: actions/checkout@v4 + - name: Prepare + run: | + platform=${{ matrix.platform }} + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY_IMAGE }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ vars.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push by digest + id: build + uses: docker/build-push-action@v6 + with: + platforms: ${{ matrix.platform }} + labels: ${{ steps.meta.outputs.labels }} + outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true + + - name: Export digest + run: | + mkdir -p /tmp/digests + digest="${{ steps.build.outputs.digest }}" + touch "/tmp/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ env.PLATFORM_PAIR }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + merge: + runs-on: ubuntu-latest + needs: + - build-docker + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: /tmp/digests + pattern: digests-* + merge-multiple: true + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY_IMAGE }} + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ vars.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Create manifest list and push + working-directory: /tmp/digests + run: | + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) + + - name: Inspect image + run: | + docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }} build: - name: Build + name: Build Binary needs: - test concurrency: diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b9ff62f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +FROM golang:1.22.5-bookworm AS builder + +WORKDIR /app + +COPY go.mod . +COPY go.sum . +RUN go mod download + +COPY . . + +RUN go build -o /iceperf-agent cmd/iceperf/main.go + +FROM debian:bookworm-slim + +WORKDIR / + +RUN apt-get update && apt-get install -y ca-certificates + +COPY --from=builder /iceperf-agent . + +RUN ls -lsa / +COPY config-api.yaml config.yaml + +ENTRYPOINT ["./iceperf-agent"] +CMD ["-config", "config.yaml"] From fcfd4d6cc25ee8070135c97c871b85bfb52830de Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Sun, 4 Aug 2024 16:59:31 +0100 Subject: [PATCH 15/21] fix the docker file --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b9ff62f..8d276c3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,7 +19,6 @@ RUN apt-get update && apt-get install -y ca-certificates COPY --from=builder /iceperf-agent . RUN ls -lsa / -COPY config-api.yaml config.yaml ENTRYPOINT ["./iceperf-agent"] CMD ["-config", "config.yaml"] From 0c02d1666eb24f54655798a77f485d3308142e51 Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Sun, 4 Aug 2024 17:02:44 +0100 Subject: [PATCH 16/21] ok try one last time --- .github/workflows/go.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 5b88c60..7c9a3a1 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -58,11 +58,9 @@ jobs: matrix: platform: - linux/amd64 - - linux/arm/v6 + # - linux/arm/v6 - linux/arm/v7 - linux/arm64 - - darwin/arm64 - steps: - uses: actions/checkout@v4 - name: Prepare @@ -178,6 +176,7 @@ jobs: goos: ${{ matrix.goos }} goarch: ${{ matrix.goarch }} project_path: "./cmd/iceperf" + overwrite: true # - name: Build # run: go build -o ${{ matrix.binary-name }} ./cmd/iceperf From fa94c293740a34dcd51bf13acc5d0167aa8e8e4c Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Mon, 5 Aug 2024 21:04:45 +0100 Subject: [PATCH 17/21] deal with api cli params, risc-v --- .github/workflows/go.yml | 3 +- cmd/iceperf/main.go | 41 +++++++++++++++++++++++ config/config.go | 70 ++++++++++++++++++++++++++++++++-------- 3 files changed, 100 insertions(+), 14 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 7c9a3a1..eb4d469 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -61,6 +61,7 @@ jobs: # - linux/arm/v6 - linux/arm/v7 - linux/arm64 + - linux/riscv64 steps: - uses: actions/checkout@v4 - name: Prepare @@ -155,7 +156,7 @@ jobs: matrix: # build and publish in parallel: linux/386, linux/amd64, linux/arm64, windows/386, windows/amd64, darwin/amd64, darwin/arm64 goos: [linux, windows, darwin] - goarch: [amd64, arm64] + goarch: [amd64, arm64, riscv64] exclude: - goarch: arm64 goos: windows diff --git a/cmd/iceperf/main.go b/cmd/iceperf/main.go index 74fbbce..6dd30f6 100644 --- a/cmd/iceperf/main.go +++ b/cmd/iceperf/main.go @@ -46,6 +46,22 @@ func main() { Aliases: []string{"c"}, Usage: "ICEPerf yaml config file", }, + &cli.StringFlag{ + Name: "api-uri", + Aliases: []string{"a"}, + Usage: "API URI", + }, + &cli.StringFlag{ + Name: "api-key", + Aliases: []string{"k"}, + Usage: "API Key", + }, + &cli.BoolFlag{ + Name: "timer", + Aliases: []string{"t"}, + Value: false, + Usage: "Enable Timer Mode", + }, }, Action: runService, } @@ -410,6 +426,31 @@ func getConfig(c *cli.Context) (*config.Config, error) { return nil, err } + //if we got passed in the api host and the api key then overwrite the config + //same for timer mode + if c.String("api-uri") != "" { + conf.Api.Enabled = true + conf.Api.URI = c.String("api-uri") + } + + if c.String("api-key") != "" { + conf.Api.Enabled = true + conf.Api.ApiKey = c.String("api-key") + } + + if conf.Api.Enabled && conf.Api.URI == "" { + conf.Api.URI = "https://api.iceperf.com/api/settings" + } + + if c.Bool("timer") { + conf.Timer.Enabled = true + conf.Timer.Interval = 60 + } + + if conf.Api.Enabled && conf.Api.ApiKey != "" && conf.Api.URI != "" { + conf.UpdateConfigFromApi() + } + return conf, nil } diff --git a/config/config.go b/config/config.go index f7f14b7..12c3efb 100644 --- a/config/config.go +++ b/config/config.go @@ -1,7 +1,11 @@ package config import ( + "encoding/json" + "errors" + "io" "log/slog" + "net/http" "github.com/pion/webrtc/v4" "github.com/prometheus/client_golang/prometheus" @@ -11,9 +15,9 @@ import ( type ICEConfig struct { Username string `yaml:"username,omitempty"` Password string `yaml:"password,omitempty"` - ApiKey string `yaml:"api_key,omitempty"` + ApiKey string `json:"apiKey,omitempty" yaml:"api_key,omitempty"` AccountSid string `yaml:"account_sid,omitempty"` - RequestUrl string `yaml:"request_url,omitempty"` + RequestUrl string `json:"requestUrl,omitempty" yaml:"request_url,omitempty"` HttpUsername string `yaml:"http_username"` HttpPassword string `yaml:"http_password"` Enabled bool `yaml:"enabled"` @@ -27,12 +31,12 @@ type ICEConfig struct { } type LokiConfig struct { - Enabled bool `yaml:"enabled"` + Enabled bool `json:"enabled" yaml:"enabled"` UseBasicAuth bool `yaml:"use_basic_auth"` UseHeadersAuth bool `yaml:"use_headers_auth"` Username string `yaml:"username,omitempty"` Password string `yaml:"password,omitempty"` - URL string `yaml:"url"` + URL string `json:"url" yaml:"url"` AuthHeaders map[string]string `yaml:"auth_headers,omitempty"` } @@ -43,9 +47,9 @@ type PromConfig struct { } type ApiConfig struct { - Enabled bool `yaml:"enabled"` - URI string `yaml:"uri"` - ApiKey string `yaml:"api_key,omitempty"` + Enabled bool `json:"enabled" yaml:"enabled"` + URI string `json:"uri" yaml:"uri"` + ApiKey string `json:"apiKey,omitempty" yaml:"api_key,omitempty"` } type LoggingConfig struct { @@ -56,15 +60,16 @@ type LoggingConfig struct { } type TimerConfig struct { - Enabled bool `yaml:"enabled"` - Interval int `yaml:"interval"` + Enabled bool `json:"enabled" yaml:"enabled"` + Interval int `json:"interval" yaml:"interval"` } type Config struct { - NodeID string `yaml:"node_id"` - ICEConfig map[string]ICEConfig `yaml:"ice_servers"` - Logging LoggingConfig `yaml:"logging"` - Timer TimerConfig `yaml:"timer"` + NodeID string `json:"nodeId" yaml:"node_id"` + ICEConfig map[string]ICEConfig `json:"iceServers" yaml:"ice_servers"` + Logging LoggingConfig `json:"logging" yaml:"logging"` + Timer TimerConfig `json:"timer" yaml:"timer"` + Api ApiConfig `json:"api" yaml:"api"` WebRTCConfig webrtc.Configuration // TODO the following should be different for answerer and offerer sides @@ -88,3 +93,42 @@ func NewConfig(confString string) (*Config, error) { } return c, nil } + +func (c *Config) UpdateConfigFromApi() error { + httpClient := &http.Client{} + + req, err := http.NewRequest("GET", c.Api.URI, nil) + req.Header.Add("Content-Type", "application/json") + req.Header.Add("Authorization", "Bearer "+c.Api.ApiKey) + + if err != nil { + return err + } + + res, err := httpClient.Do(req) + if err != nil { + return err + } + + defer res.Body.Close() + //check the code of the response + if res.StatusCode != 200 { + err = errors.New("error from our api " + res.Status) + return err + } + + responseData, err := io.ReadAll(res.Body) + if err != nil { + return err + } + responseConfig := Config{} + json.Unmarshal([]byte(responseData), &responseConfig) + + //go and merge in values from the API into the config + + //lets just do the basics for now.... + + c.ICEConfig = responseConfig.ICEConfig + + return nil +} From f8421b97c0833cdda7208723437e46cf54e4fc10 Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Mon, 5 Aug 2024 21:14:02 +0100 Subject: [PATCH 18/21] alpine image --- .github/workflows/go.yml | 4 ++++ Dockerfile | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index eb4d469..a744193 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -160,6 +160,10 @@ jobs: exclude: - goarch: arm64 goos: windows + - goarch: riscv64 + goos: windows + - goarch: riscv64 + goos: darwin steps: - uses: actions/checkout@v4 diff --git a/Dockerfile b/Dockerfile index 8d276c3..422091c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.22.5-bookworm AS builder +FROM golang:1.22.5 AS builder WORKDIR /app @@ -10,7 +10,7 @@ COPY . . RUN go build -o /iceperf-agent cmd/iceperf/main.go -FROM debian:bookworm-slim +FROM alpine WORKDIR / From e5a33cdaf973446e7df25c3292674c3f5db3091c Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Mon, 5 Aug 2024 21:47:41 +0100 Subject: [PATCH 19/21] remove update certs --- Dockerfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 422091c..5952f05 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,8 +14,6 @@ FROM alpine WORKDIR / -RUN apt-get update && apt-get install -y ca-certificates - COPY --from=builder /iceperf-agent . RUN ls -lsa / From fd78ef9d97dbbe894bcecba5f760b19c021856e8 Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Mon, 5 Aug 2024 22:14:54 +0100 Subject: [PATCH 20/21] fix image --- .github/workflows/go.yml | 2 +- Dockerfile | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index a744193..d5c64f7 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -61,7 +61,7 @@ jobs: # - linux/arm/v6 - linux/arm/v7 - linux/arm64 - - linux/riscv64 + # - linux/riscv64 steps: - uses: actions/checkout@v4 - name: Prepare diff --git a/Dockerfile b/Dockerfile index 5952f05..ac3d43e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.22.5 AS builder +FROM golang:1.22.5-bullseye AS builder WORKDIR /app @@ -10,10 +10,12 @@ COPY . . RUN go build -o /iceperf-agent cmd/iceperf/main.go -FROM alpine +FROM debian:bullseye-slim WORKDIR / +RUN apt-get update && apt-get install -y ca-certificates + COPY --from=builder /iceperf-agent . RUN ls -lsa / From 6cc0fdeeb835f65e0d4e2a401a2eede3737c1088 Mon Sep 17 00:00:00 2001 From: Dan Jenkins Date: Tue, 6 Aug 2024 10:22:19 +0100 Subject: [PATCH 21/21] final docker changes --- config/config.go | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index 12c3efb..dffba1c 100644 --- a/config/config.go +++ b/config/config.go @@ -6,6 +6,7 @@ import ( "io" "log/slog" "net/http" + "reflect" "github.com/pion/webrtc/v4" "github.com/prometheus/client_golang/prometheus" @@ -82,6 +83,41 @@ type Config struct { Registry *prometheus.Registry } +func mergeConfigs(c, responseConfig interface{}) { + mergeStructs(reflect.ValueOf(c).Elem(), reflect.ValueOf(responseConfig).Elem()) +} + +func mergeStructs(cValue, respValue reflect.Value) { + for i := 0; i < respValue.NumField(); i++ { + respField := respValue.Field(i) + cField := cValue.Field(i) + + if !respField.IsZero() { + switch respField.Kind() { + case reflect.Ptr: + if !respField.IsNil() { + if cField.IsNil() { + cField.Set(reflect.New(cField.Type().Elem())) + } + mergeStructs(cField.Elem(), respField.Elem()) + } + case reflect.Struct: + mergeStructs(cField, respField) + case reflect.Map: + if cField.IsNil() { + cField.Set(reflect.MakeMap(cField.Type())) + } + for _, key := range respField.MapKeys() { + val := respField.MapIndex(key) + cField.SetMapIndex(key, val) + } + default: + cField.Set(respField) + } + } + } +} + func NewConfig(confString string) (*Config, error) { c := &Config{ ServiceName: "ICEPerf", @@ -127,8 +163,8 @@ func (c *Config) UpdateConfigFromApi() error { //go and merge in values from the API into the config //lets just do the basics for now.... - + //this needs a lot more work c.ICEConfig = responseConfig.ICEConfig - + // mergeConfigs(c, responseConfig) return nil }