Skip to content

Commit

Permalink
Publish DNS Goose
Browse files Browse the repository at this point in the history
The internal and external repositories are out of sync. This Pull Request attempts to brings them back in sync by patching the GitHub repository. Please carefully review this patch. You must disable ShipIt for your project in order to merge this pull request. DO NOT IMPORT this pull request. Instead, merge it directly on GitHub using the MERGE BUTTON. Re-enable ShipIt after merging.
  • Loading branch information
facebook-github-bot committed Oct 5, 2023
1 parent 68b977e commit 05c7785
Show file tree
Hide file tree
Showing 15 changed files with 1,236 additions and 0 deletions.
32 changes: 32 additions & 0 deletions goose/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module github.com/facebook/dns/goose

go 1.19

require (
github.com/miekg/dns v1.1.55
github.com/prometheus/client_golang v1.17.0
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.8.4
go.uber.org/ratelimit v0.3.0
gonum.org/v1/gonum v0.14.0
)

require (
github.com/benbjohnson/clock v1.3.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect
golang.org/x/mod v0.9.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/tools v0.7.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
67 changes: 67 additions & 0 deletions goose/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw=
go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0=
gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
260 changes: 260 additions & 0 deletions goose/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
package main

import (
"flag"
"fmt"
"math/rand"
"net/http"
_ "net/http/pprof"
"os"
"os/signal"
"sync"
"syscall"
"time"

"github.com/facebook/dns/goose/query"
"github.com/facebook/dns/goose/report"
"github.com/facebook/dns/goose/stats"
"github.com/miekg/dns"

"go.uber.org/ratelimit"

log "github.com/sirupsen/logrus"
)

const pprofHTTP = "localhost:6060"

var (
daemon bool
logLevel string
totalQueries int
dport int
// names string
domain string
host string
logging bool
timeout time.Duration
duration time.Duration
samplingInterval time.Duration
debugger bool
maxqps int
randomiseQueries bool
qTypeStr string
monitorPort int
monitorHost string
parallelConnections int
reportJSON bool
inputFile string
exporterAddr string
)

func main() {
flag.BoolVar(&daemon, "daemon", false,
"Running in daemon mode means that metrics will be exported rather than printed to stdout")
flag.StringVar(&logLevel, "loglevel", "info", "Set a log level. Can be: debug, info, warning, error")
flag.IntVar(&totalQueries, "total-queries", 50000, "Total queries to send")
flag.IntVar(&dport, "port", 53, "destination port")
flag.StringVar(&domain, "domain", "", "Domain for uncached queries")
flag.StringVar(&qTypeStr, "query-type", "A", "Query type to be used for the query")
flag.StringVar(&inputFile, "input-file", "", "The file that contains queries to be made in qname qtype format")
flag.StringVar(&host, "host", "127.0.0.1", "IP address of DNS server to test")
flag.BoolVar(&logging, "enable-logging", true, "Whether to enable logging or not")
flag.BoolVar(&randomiseQueries, "randomise-queries", false, "Whether to randomise dns queries to bypass potential caching")
flag.IntVar(&monitorPort, "monitor-port", 8953, "DNS queries not sent if this port is down on the monitored host (defaults to unbound remote-control port)")
flag.StringVar(&monitorHost, "monitor-host", "127.0.0.1", "DNS queries not sent if the monitored port on this host is down")
flag.StringVar(&exporterAddr, "exporter-addr", ":6869", "Exporter bind address")
flag.DurationVar(&duration, "max-duration", 0*time.Second, "Maximum duration of test (seconds)")
flag.DurationVar(&timeout, "timeout", 3*time.Second, "Duration of timeout for queries")
flag.DurationVar(&samplingInterval, "sample", 0*time.Second, "Sampling frequency for reporting (seconds)")
flag.BoolVar(&debugger, "pprof", false, "Enable pprof")
flag.IntVar(&maxqps, "max-qps", 0, "max number of QPS")
flag.IntVar(&parallelConnections, "parallel-connections", 1, "max number of parallel connections")
flag.BoolVar(&reportJSON, "report-json", false, "Report run results to stdout in json format")
flag.Parse()

// Set the seed
rand.Seed(time.Now().UTC().UnixNano())

switch logLevel {
case "debug":
log.SetLevel(log.DebugLevel)
case "info":
log.SetLevel(log.InfoLevel)
case "warning":
log.SetLevel(log.WarnLevel)
case "error":
log.SetLevel(log.ErrorLevel)
default:
log.Fatalf("Unrecognized log level: %v", logLevel)
}

if domain == "" && inputFile == "" {
log.Fatal("Need to specify either domain or input file, neither is specified")

}
if domain != "" && inputFile != "" {
log.Fatal("Need to specify either domain or input file, both are specified, please only specify one of them")

}
qnames := make([]string, 0)
qtypes := make([]dns.Type, 0)
var err error
if inputFile != "" {
qnames, qtypes, err = query.ProcessQueryInputFile(inputFile)
if err != nil {
log.Fatalf("Failed to process query input file: %s %v", inputFile, err)
}
} else {
qnames = []string{domain}
qType, qtypeErr := query.QTypeStrToDNSQtype(qTypeStr)
if qtypeErr != nil {
log.Fatalf("%v", err)
}
qtypes = []dns.Type{qType}
}
sigStop := make(chan os.Signal, 1)
sigPause := make(chan struct{}, 1)

var reporter stats.Reporter = &report.LogStatsReporter{}
if reportJSON {
reporter = &report.JSONStatsReporter{}
}
// Do not stop unless interrupted
signal.Notify(sigStop, syscall.SIGINT) // ^C (Control-C).
signal.Notify(sigStop, syscall.SIGQUIT) // ^\ (Control-Backslash)
signal.Notify(sigStop, syscall.SIGTERM) // kill/pkill etc

if debugger {
log.Infof("Starting profiler on %s", pprofHTTP)
go func() {
listenError := http.ListenAndServe(pprofHTTP, nil)
if listenError != nil {
log.Errorf("failed to start pprof: %s", listenError.Error())
}
}()
}

if daemon {
log.Infof("Running in daemon mode")
log.Infof("Monitor Host/Port is %s:%d", monitorHost, monitorPort)
query.MonitorTarget(sigPause, monitorPort, monitorHost)
// @fb-only: reporter = &report.ODSMetricsReporter{Prefix: "dns.goose", Addr: exporterAddr}
reporter = &report.PrometheusMetricsReporter{Addr: exporterAddr} // @oss-only

// Do nothing on SIGHUP (terminal disconnect)
signal.Notify(make(chan os.Signal, 1), syscall.SIGHUP)

} else {
log.Infof("The total number of DNS requests will be: %v", totalQueries)
}

goosestr := `
_______________________________
< Honking at %s:%d with %s QPS.>
--------------------------------
\
\
\
___
.^ ""-.
_.-^( e _ '.
'-===.>_.-^ '. "
" "
: "
: | __.--._
| '--" ""-._ _.^)
/ ""-^ _>
: -^>
: .__> __)
\ '._ .__.-' .-'
'.___ '-.__.-' /
'-.__ . _.' /
\_____> >'.__/_.""
.'.----' | |
.' / | |
'^-/ ___| :
>-- /
>.'.'
'-^`
go func() {
reporterInitError := reporter.Initialize()
if reporterInitError != nil {
log.Errorf("Failed to initialize stats reporter %v", reporterInitError)
}
}()
var rate ratelimit.Limiter
qpsStr := "Unlimited"
if maxqps > 0 {
log.Infof("Limiting max qps to: %d", maxqps)
rate = ratelimit.New(maxqps)
qpsStr = fmt.Sprint(maxqps)
} else {
rate = ratelimit.NewUnlimited()
}
log.Infof(goosestr, host, dport, qpsStr)
runState := query.NewRunState(totalQueries, rate, daemon, time.Now)
if duration != 0 && !daemon {
timer := time.NewTimer(duration)
go func() {
<-timer.C
log.Info("Max duration reached. Exiting.")
close(sigStop)
}()
}
periodicReporterStop := make(chan os.Signal, 1)
if samplingInterval != 0 {
log.Infof("Sampling interval is %v", samplingInterval)
samplingTicker := time.NewTicker(samplingInterval)
go func() {
for {
select {
case <-samplingTicker.C:
sampledMetrics := runState.ExportIntermediateResults()
repErr := reporter.ReportMetrics(sampledMetrics)
if err != nil {
log.Errorf("Failed to report metrics %v", repErr)
}
case <-periodicReporterStop:
return
}
}
}()
}

go func() {

log.Infof(
"Starting the test and running %d connections in parallel",
parallelConnections,
)
var wg sync.WaitGroup
for i := 0; i < parallelConnections; i++ {
wg.Add(1)
go func() {
qErr := query.RunQueries(dport, host, qnames, timeout, randomiseQueries, qtypes, time.Now, runState, sigPause)
if err != nil {
log.Errorf("Failed to run queries %v", qErr)
}
wg.Done()
}()
}
wg.Wait()

log.Info("Finished running all connections")
close(sigStop)
}()
select { // nolint: gosimple
case <-sigStop:
log.Infof("No more requests will be sent")
close(periodicReporterStop)
}
if !daemon {
log.Info("The test results are:")

finishedMetrics := runState.ExportResults()
err = reporter.ReportMetrics(finishedMetrics)
if err != nil {
log.Errorf("Failed to report metrics %v", err)
}
}
}
Loading

0 comments on commit 05c7785

Please sign in to comment.