diff --git a/backend/server/server.go b/backend/server/server.go index fd67933..bec4b55 100644 --- a/backend/server/server.go +++ b/backend/server/server.go @@ -1,6 +1,7 @@ package main import ( + "context" "encoding/json" "fmt" "html" @@ -8,16 +9,26 @@ import ( "log" "net/http" "os" + "reflect" "runtime" "strconv" "strings" "time" + "github.com/DataDog/datadog-go/statsd" "github.com/ddworken/hishtory/shared" + "github.com/jackc/pgx/v4/stdlib" _ "github.com/lib/pq" "github.com/rodaine/table" - "gorm.io/driver/postgres" + sqltrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql" + gormtrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/gorm.io/gorm.v1" + httptrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" + "gopkg.in/DataDog/dd-trace-go.v1/profiler" + "gorm.io/gorm/logger" + "gorm.io/driver/postgres" "gorm.io/driver/sqlite" "gorm.io/gorm" ) @@ -28,6 +39,7 @@ const ( var ( GLOBAL_DB *gorm.DB + GLOBAL_STATSD *statsd.Client ReleaseVersion string = "UNKNOWN" ) @@ -54,27 +66,27 @@ func getHishtoryVersion(r *http.Request) string { return r.Header.Get("X-Hishtory-Version") } -func updateUsageData(r *http.Request, userId, deviceId string, numEntriesHandled int, isQuery bool) { +func updateUsageData(ctx context.Context, r *http.Request, userId, deviceId string, numEntriesHandled int, isQuery bool) { var usageData []UsageData - GLOBAL_DB.Where("user_id = ? AND device_id = ?", userId, deviceId).Find(&usageData) + GLOBAL_DB.WithContext(ctx).Where("user_id = ? AND device_id = ?", userId, deviceId).Find(&usageData) if len(usageData) == 0 { - GLOBAL_DB.Create(&UsageData{UserId: userId, DeviceId: deviceId, LastUsed: time.Now(), NumEntriesHandled: numEntriesHandled, Version: getHishtoryVersion(r)}) + GLOBAL_DB.WithContext(ctx).Create(&UsageData{UserId: userId, DeviceId: deviceId, LastUsed: time.Now(), NumEntriesHandled: numEntriesHandled, Version: getHishtoryVersion(r)}) } else { usage := usageData[0] - GLOBAL_DB.Model(&UsageData{}).Where("user_id = ? AND device_id = ?", userId, deviceId).Update("last_used", time.Now()).Update("last_ip", getRemoteAddr(r)) + GLOBAL_DB.WithContext(ctx).Model(&UsageData{}).Where("user_id = ? AND device_id = ?", userId, deviceId).Update("last_used", time.Now()).Update("last_ip", getRemoteAddr(r)) if numEntriesHandled > 0 { - GLOBAL_DB.Exec("UPDATE usage_data SET num_entries_handled = COALESCE(num_entries_handled, 0) + ? WHERE user_id = ? AND device_id = ?", numEntriesHandled, userId, deviceId) + GLOBAL_DB.WithContext(ctx).Exec("UPDATE usage_data SET num_entries_handled = COALESCE(num_entries_handled, 0) + ? WHERE user_id = ? AND device_id = ?", numEntriesHandled, userId, deviceId) } if usage.Version != getHishtoryVersion(r) { - GLOBAL_DB.Exec("UPDATE usage_data SET version = ? WHERE user_id = ? AND device_id = ?", getHishtoryVersion(r), userId, deviceId) + GLOBAL_DB.WithContext(ctx).Exec("UPDATE usage_data SET version = ? WHERE user_id = ? AND device_id = ?", getHishtoryVersion(r), userId, deviceId) } } if isQuery { - GLOBAL_DB.Exec("UPDATE usage_data SET num_queries = COALESCE(num_queries, 0) + 1, last_queried = ? WHERE user_id = ? AND device_id = ?", time.Now(), userId, deviceId) + GLOBAL_DB.WithContext(ctx).Exec("UPDATE usage_data SET num_queries = COALESCE(num_queries, 0) + 1, last_queried = ? WHERE user_id = ? AND device_id = ?", time.Now(), userId, deviceId) } } -func usageStatsHandler(w http.ResponseWriter, r *http.Request) { +func usageStatsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { query := ` SELECT MIN(devices.registration_date) as registration_date, @@ -90,7 +102,7 @@ func usageStatsHandler(w http.ResponseWriter, r *http.Request) { GROUP BY devices.user_id ORDER BY registration_date ` - rows, err := GLOBAL_DB.Raw(query).Rows() + rows, err := GLOBAL_DB.WithContext(ctx).Raw(query).Rows() if err != nil { panic(err) } @@ -116,24 +128,24 @@ func usageStatsHandler(w http.ResponseWriter, r *http.Request) { tbl.Print() } -func statsHandler(w http.ResponseWriter, r *http.Request) { +func statsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { var numDevices int64 = 0 - checkGormResult(GLOBAL_DB.Model(&shared.Device{}).Count(&numDevices)) + checkGormResult(GLOBAL_DB.WithContext(ctx).Model(&shared.Device{}).Count(&numDevices)) type numEntriesProcessed struct { Total int } nep := numEntriesProcessed{} - checkGormResult(GLOBAL_DB.Model(&UsageData{}).Select("SUM(num_entries_handled) as total").Find(&nep)) + checkGormResult(GLOBAL_DB.WithContext(ctx).Model(&UsageData{}).Select("SUM(num_entries_handled) as total").Find(&nep)) var numDbEntries int64 = 0 - checkGormResult(GLOBAL_DB.Model(&shared.EncHistoryEntry{}).Count(&numDbEntries)) + checkGormResult(GLOBAL_DB.WithContext(ctx).Model(&shared.EncHistoryEntry{}).Count(&numDbEntries)) lastWeek := time.Now().AddDate(0, 0, -7) var weeklyActiveInstalls int64 = 0 - checkGormResult(GLOBAL_DB.Model(&UsageData{}).Where("last_used > ?", lastWeek).Count(&weeklyActiveInstalls)) + checkGormResult(GLOBAL_DB.WithContext(ctx).Model(&UsageData{}).Where("last_used > ?", lastWeek).Count(&weeklyActiveInstalls)) var weeklyQueryUsers int64 = 0 - checkGormResult(GLOBAL_DB.Model(&UsageData{}).Where("last_queried > ?", lastWeek).Count(&weeklyQueryUsers)) + checkGormResult(GLOBAL_DB.WithContext(ctx).Model(&UsageData{}).Where("last_queried > ?", lastWeek).Count(&weeklyQueryUsers)) var lastRegistration string = "" - err := GLOBAL_DB.Raw("select to_char(max(registration_date), 'DD Month YYYY HH24:MI') from devices").Row().Scan(&lastRegistration) + err := GLOBAL_DB.WithContext(ctx).Raw("select to_char(max(registration_date), 'DD Month YYYY HH24:MI') from devices").Row().Scan(&lastRegistration) if err != nil { panic(err) } @@ -145,7 +157,7 @@ func statsHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte(fmt.Sprintf("Last registration: %s\n", lastRegistration))) } -func apiSubmitHandler(w http.ResponseWriter, r *http.Request) { +func apiSubmitHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { data, err := ioutil.ReadAll(r.Body) if err != nil { panic(err) @@ -159,8 +171,8 @@ func apiSubmitHandler(w http.ResponseWriter, r *http.Request) { if len(entries) == 0 { return } - updateUsageData(r, entries[0].UserId, entries[0].DeviceId, len(entries), false) - tx := GLOBAL_DB.Where("user_id = ?", entries[0].UserId) + updateUsageData(ctx, r, entries[0].UserId, entries[0].DeviceId, len(entries), false) + tx := GLOBAL_DB.WithContext(ctx).Where("user_id = ?", entries[0].UserId) var devices []*shared.Device checkGormResult(tx.Find(&devices)) if len(devices) == 0 { @@ -171,15 +183,16 @@ func apiSubmitHandler(w http.ResponseWriter, r *http.Request) { for _, entry := range entries { entry.DeviceId = device.DeviceId } - checkGormResult(GLOBAL_DB.Create(&entries)) + checkGormResult(GLOBAL_DB.WithContext(ctx).Create(&entries)) } + GLOBAL_STATSD.Count("hishtory.submit", int64(len(devices)), []string{}, 1.0) } -func apiBootstrapHandler(w http.ResponseWriter, r *http.Request) { +func apiBootstrapHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { userId := getRequiredQueryParam(r, "user_id") deviceId := getRequiredQueryParam(r, "device_id") - updateUsageData(r, userId, deviceId, 0, false) - tx := GLOBAL_DB.Where("user_id = ?", userId) + updateUsageData(ctx, r, userId, deviceId, 0, false) + tx := GLOBAL_DB.WithContext(ctx).Where("user_id = ?", userId) var historyEntries []*shared.EncHistoryEntry checkGormResult(tx.Find(&historyEntries)) fmt.Printf("apiBootstrapHandler: Found %d entries\n", len(historyEntries)) @@ -190,25 +203,25 @@ func apiBootstrapHandler(w http.ResponseWriter, r *http.Request) { w.Write(resp) } -func apiQueryHandler(w http.ResponseWriter, r *http.Request) { +func apiQueryHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { userId := getRequiredQueryParam(r, "user_id") deviceId := getRequiredQueryParam(r, "device_id") - updateUsageData(r, userId, deviceId, 0, true) + updateUsageData(ctx, r, userId, deviceId, 0, true) // Increment the count - checkGormResult(GLOBAL_DB.Exec("UPDATE enc_history_entries SET read_count = read_count + 1 WHERE device_id = ?", deviceId)) + checkGormResult(GLOBAL_DB.WithContext(ctx).Exec("UPDATE enc_history_entries SET read_count = read_count + 1 WHERE device_id = ?", deviceId)) // Delete any entries that match a pending deletion request var deletionRequests []*shared.DeletionRequest - checkGormResult(GLOBAL_DB.Where("destination_device_id = ? AND user_id = ?", deviceId, userId).Find(&deletionRequests)) + checkGormResult(GLOBAL_DB.WithContext(ctx).Where("destination_device_id = ? AND user_id = ?", deviceId, userId).Find(&deletionRequests)) for _, request := range deletionRequests { - _, err := applyDeletionRequestsToBackend(*request) + _, err := applyDeletionRequestsToBackend(ctx, *request) if err != nil { panic(err) } } // Then retrieve, to avoid a race condition - tx := GLOBAL_DB.Where("device_id = ? AND read_count < 5", deviceId) + tx := GLOBAL_DB.WithContext(ctx).Where("device_id = ? AND read_count < 5", deviceId) var historyEntries []*shared.EncHistoryEntry checkGormResult(tx.Find(&historyEntries)) fmt.Printf("apiQueryHandler: Found %d entries for %s\n", len(historyEntries), r.URL) @@ -217,6 +230,7 @@ func apiQueryHandler(w http.ResponseWriter, r *http.Request) { panic(err) } w.Write(resp) + GLOBAL_STATSD.Incr("hishtory.query", []string{}, 1.0) } func getRemoteAddr(r *http.Request) string { @@ -227,25 +241,26 @@ func getRemoteAddr(r *http.Request) string { return addr[0] } -func apiRegisterHandler(w http.ResponseWriter, r *http.Request) { +func apiRegisterHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { userId := getRequiredQueryParam(r, "user_id") deviceId := getRequiredQueryParam(r, "device_id") var existingDevicesCount int64 = -1 - checkGormResult(GLOBAL_DB.Model(&shared.Device{}).Where("user_id = ?", userId).Count(&existingDevicesCount)) + checkGormResult(GLOBAL_DB.WithContext(ctx).Model(&shared.Device{}).Where("user_id = ?", userId).Count(&existingDevicesCount)) fmt.Printf("apiRegisterHandler: existingDevicesCount=%d\n", existingDevicesCount) - checkGormResult(GLOBAL_DB.Create(&shared.Device{UserId: userId, DeviceId: deviceId, RegistrationIp: getRemoteAddr(r), RegistrationDate: time.Now()})) + checkGormResult(GLOBAL_DB.WithContext(ctx).Create(&shared.Device{UserId: userId, DeviceId: deviceId, RegistrationIp: getRemoteAddr(r), RegistrationDate: time.Now()})) if existingDevicesCount > 0 { - checkGormResult(GLOBAL_DB.Create(&shared.DumpRequest{UserId: userId, RequestingDeviceId: deviceId, RequestTime: time.Now()})) + checkGormResult(GLOBAL_DB.WithContext(ctx).Create(&shared.DumpRequest{UserId: userId, RequestingDeviceId: deviceId, RequestTime: time.Now()})) } - updateUsageData(r, userId, deviceId, 0, false) + updateUsageData(ctx, r, userId, deviceId, 0, false) + GLOBAL_STATSD.Incr("hishtory.register", []string{}, 1.0) } -func apiGetPendingDumpRequestsHandler(w http.ResponseWriter, r *http.Request) { +func apiGetPendingDumpRequestsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { userId := getRequiredQueryParam(r, "user_id") deviceId := getRequiredQueryParam(r, "device_id") var dumpRequests []*shared.DumpRequest // Filter out ones requested by the hishtory instance that sent this request - checkGormResult(GLOBAL_DB.Where("user_id = ? AND requesting_device_id != ?", userId, deviceId).Find(&dumpRequests)) + checkGormResult(GLOBAL_DB.WithContext(ctx).Where("user_id = ? AND requesting_device_id != ?", userId, deviceId).Find(&dumpRequests)) respBody, err := json.Marshal(dumpRequests) if err != nil { panic(fmt.Errorf("failed to JSON marshall the dump requests: %v", err)) @@ -253,7 +268,7 @@ func apiGetPendingDumpRequestsHandler(w http.ResponseWriter, r *http.Request) { w.Write(respBody) } -func apiSubmitDumpHandler(w http.ResponseWriter, r *http.Request) { +func apiSubmitDumpHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { userId := getRequiredQueryParam(r, "user_id") srcDeviceId := getRequiredQueryParam(r, "source_device_id") requestingDeviceId := getRequiredQueryParam(r, "requesting_device_id") @@ -267,7 +282,7 @@ func apiSubmitDumpHandler(w http.ResponseWriter, r *http.Request) { panic(fmt.Sprintf("body=%#v, err=%v", data, err)) } fmt.Printf("apiSubmitDumpHandler: received request containg %d EncHistoryEntry\n", len(entries)) - err = GLOBAL_DB.Transaction(func(tx *gorm.DB) error { + err = GLOBAL_DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error { for _, entry := range entries { entry.DeviceId = requestingDeviceId if entry.UserId != userId { @@ -280,11 +295,11 @@ func apiSubmitDumpHandler(w http.ResponseWriter, r *http.Request) { if err != nil { panic(fmt.Errorf("failed to execute transaction to add dumped DB: %v", err)) } - checkGormResult(GLOBAL_DB.Delete(&shared.DumpRequest{}, "user_id = ? AND requesting_device_id = ?", userId, requestingDeviceId)) - updateUsageData(r, userId, srcDeviceId, len(entries), false) + checkGormResult(GLOBAL_DB.WithContext(ctx).Delete(&shared.DumpRequest{}, "user_id = ? AND requesting_device_id = ?", userId, requestingDeviceId)) + updateUsageData(ctx, r, userId, srcDeviceId, len(entries), false) } -func apiBannerHandler(w http.ResponseWriter, r *http.Request) { +func apiBannerHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { commitHash := getRequiredQueryParam(r, "commit_hash") deviceId := getRequiredQueryParam(r, "device_id") forcedBanner := r.URL.Query().Get("forced_banner") @@ -296,16 +311,16 @@ func apiBannerHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte(html.EscapeString(forcedBanner))) } -func getDeletionRequestsHandler(w http.ResponseWriter, r *http.Request) { +func getDeletionRequestsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { userId := getRequiredQueryParam(r, "user_id") deviceId := getRequiredQueryParam(r, "device_id") // Increment the ReadCount - checkGormResult(GLOBAL_DB.Exec("UPDATE deletion_requests SET read_count = read_count + 1 WHERE destination_device_id = ? AND user_id = ?", deviceId, userId)) + checkGormResult(GLOBAL_DB.WithContext(ctx).Exec("UPDATE deletion_requests SET read_count = read_count + 1 WHERE destination_device_id = ? AND user_id = ?", deviceId, userId)) // Return all the deletion requests var deletionRequests []*shared.DeletionRequest - checkGormResult(GLOBAL_DB.Where("user_id = ? AND destination_device_id = ?", userId, deviceId).Find(&deletionRequests)) + checkGormResult(GLOBAL_DB.WithContext(ctx).Where("user_id = ? AND destination_device_id = ?", userId, deviceId).Find(&deletionRequests)) respBody, err := json.Marshal(deletionRequests) if err != nil { panic(fmt.Errorf("failed to JSON marshall the dump requests: %v", err)) @@ -313,7 +328,7 @@ func getDeletionRequestsHandler(w http.ResponseWriter, r *http.Request) { w.Write(respBody) } -func addDeletionRequestHandler(w http.ResponseWriter, r *http.Request) { +func addDeletionRequestHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { data, err := ioutil.ReadAll(r.Body) if err != nil { panic(err) @@ -327,7 +342,7 @@ func addDeletionRequestHandler(w http.ResponseWriter, r *http.Request) { fmt.Printf("addDeletionRequestHandler: received request containg %d messages to be deleted\n", len(request.Messages.Ids)) // Store the deletion request so all the devices will get it - tx := GLOBAL_DB.Where("user_id = ?", request.UserId) + tx := GLOBAL_DB.WithContext(ctx).Where("user_id = ?", request.UserId) var devices []*shared.Device checkGormResult(tx.Find(&devices)) if len(devices) == 0 { @@ -336,18 +351,18 @@ func addDeletionRequestHandler(w http.ResponseWriter, r *http.Request) { fmt.Printf("addDeletionRequestHandler: Found %d devices\n", len(devices)) for _, device := range devices { request.DestinationDeviceId = device.DeviceId - checkGormResult(GLOBAL_DB.Create(&request)) + checkGormResult(GLOBAL_DB.WithContext(ctx).Create(&request)) } // Also delete anything currently in the DB matching it - numDeleted, err := applyDeletionRequestsToBackend(request) + numDeleted, err := applyDeletionRequestsToBackend(ctx, request) if err != nil { panic(err) } fmt.Printf("addDeletionRequestHandler: Deleted %d rows in the backend\n", numDeleted) } -func healthCheckHandler(w http.ResponseWriter, r *http.Request) { +func healthCheckHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { db, err := GLOBAL_DB.DB() if err != nil { panic(fmt.Sprintf("failed to get DB: %v", err)) @@ -356,19 +371,19 @@ func healthCheckHandler(w http.ResponseWriter, r *http.Request) { if err != nil { panic(fmt.Sprintf("failed to ping DB: %v", err)) } - if isProductionEnvironment(r) { + if isProductionEnvironment() { // Check that we have a reasonable looking set of devices/entries in the DB var count int64 - checkGormResult(GLOBAL_DB.Model(&shared.EncHistoryEntry{}).Count(&count)) + checkGormResult(GLOBAL_DB.WithContext(ctx).Model(&shared.EncHistoryEntry{}).Count(&count)) if count < 1000 { panic("Suspiciously few enc history entries!") } - checkGormResult(GLOBAL_DB.Model(&shared.Device{}).Count(&count)) + checkGormResult(GLOBAL_DB.WithContext(ctx).Model(&shared.Device{}).Count(&count)) if count < 100 { panic("Suspiciously few devices!") } // Check that we can write to the DB. This entry will get written and then eventually cleaned by the cron. - checkGormResult(GLOBAL_DB.Create(&shared.EncHistoryEntry{ + checkGormResult(GLOBAL_DB.WithContext(ctx).Create(&shared.EncHistoryEntry{ EncryptedData: []byte("data"), Nonce: []byte("nonce"), DeviceId: "healthcheck_device_id", @@ -382,26 +397,29 @@ func healthCheckHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte(ok)) } -func applyDeletionRequestsToBackend(request shared.DeletionRequest) (int, error) { - tx := GLOBAL_DB.Where("false") +func applyDeletionRequestsToBackend(ctx context.Context, request shared.DeletionRequest) (int, error) { + tx := GLOBAL_DB.WithContext(ctx).Where("false") for _, message := range request.Messages.Ids { - tx = tx.Or(GLOBAL_DB.Where("user_id = ? AND device_id = ? AND date = ?", request.UserId, message.DeviceId, message.Date)) + tx = tx.Or(GLOBAL_DB.WithContext(ctx).Where("user_id = ? AND device_id = ? AND date = ?", request.UserId, message.DeviceId, message.Date)) } result := tx.Delete(&shared.EncHistoryEntry{}) checkGormResult(result) return int(result.RowsAffected), nil } -func wipeDbHandler(w http.ResponseWriter, r *http.Request) { - checkGormResult(GLOBAL_DB.Exec("DELETE FROM enc_history_entries")) +func wipeDbHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { + if r.Host == "api.hishtory.dev" { + panic("refusing to wipe the DB for prod") + } + checkGormResult(GLOBAL_DB.WithContext(ctx).Exec("DELETE FROM enc_history_entries")) } func isTestEnvironment() bool { return os.Getenv("HISHTORY_TEST") != "" } -func isProductionEnvironment(r *http.Request) bool { - return r.Host == "api.hishtory.dev" +func isProductionEnvironment() bool { + return os.Getenv("HISHTORY_ENV") == "prod" } func OpenDB() (*gorm.DB, error) { @@ -420,6 +438,14 @@ func OpenDB() (*gorm.DB, error) { return db, nil } + // The same as the default logger, except with a higher SlowThreshold + customLogger := logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Config{ + SlowThreshold: 1000 * time.Millisecond, + LogLevel: logger.Warn, + IgnoreRecordNotFoundError: false, + Colorful: true, + }) + var sqliteDb string if os.Getenv("HISHTORY_SQLITE_DB") != "" { sqliteDb = os.Getenv("HISHTORY_SQLITE_DB") @@ -429,13 +455,18 @@ func OpenDB() (*gorm.DB, error) { var err error if sqliteDb != "" { - db, err = gorm.Open(sqlite.Open(sqliteDb), &gorm.Config{}) + db, err = gorm.Open(sqlite.Open(sqliteDb), &gorm.Config{Logger: customLogger}) } else { postgresDb := fmt.Sprintf(PostgresDb, os.Getenv("POSTGRESQL_PASSWORD")) if os.Getenv("HISHTORY_POSTGRES_DB") != "" { postgresDb = os.Getenv("HISHTORY_POSTGRES_DB") } - db, err = gorm.Open(postgres.Open(postgresDb), &gorm.Config{}) + sqltrace.Register("pgx", &stdlib.Driver{}, sqltrace.WithServiceName("hishtory-api")) + sqlDb, err := sqltrace.Open("pgx", postgresDb) + if err != nil { + log.Fatal(err) + } + db, err = gormtrace.Open(postgres.New(postgres.Config{Conn: sqlDb}), &gorm.Config{Logger: customLogger}) } if err != nil { return nil, fmt.Errorf("failed to connect to the DB: %v", err) @@ -455,25 +486,25 @@ func init() { panic("server.go was built without a ReleaseVersion!") } InitDB() - go runBackgroundJobs() + go runBackgroundJobs(context.Background()) } -func cron() error { +func cron(ctx context.Context) error { err := updateReleaseVersion() if err != nil { fmt.Println(err) } - err = cleanDatabase() + err = cleanDatabase(ctx) if err != nil { fmt.Println(err) } return nil } -func runBackgroundJobs() { +func runBackgroundJobs(ctx context.Context) { time.Sleep(5 * time.Second) for { - err := cron() + err := cron(ctx) if err != nil { fmt.Printf("Cron failure: %v", err) } @@ -481,8 +512,8 @@ func runBackgroundJobs() { } } -func triggerCronHandler(w http.ResponseWriter, r *http.Request) { - err := cron() +func triggerCronHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { + err := cron(ctx) if err != nil { panic(err) } @@ -599,7 +630,7 @@ func buildUpdateInfo(version string) shared.UpdateInfo { } } -func apiDownloadHandler(w http.ResponseWriter, r *http.Request) { +func apiDownloadHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { updateInfo := buildUpdateInfo(ReleaseVersion) resp, err := json.Marshal(updateInfo) if err != nil { @@ -608,7 +639,7 @@ func apiDownloadHandler(w http.ResponseWriter, r *http.Request) { w.Write(resp) } -func slsaStatusHandler(w http.ResponseWriter, r *http.Request) { +func slsaStatusHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { // returns "OK" unless there is a current SLSA bug v := getHishtoryVersion(r) if !strings.Contains(v, "v0.") { @@ -627,7 +658,7 @@ func slsaStatusHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte("OK")) } -func feedbackHandler(w http.ResponseWriter, r *http.Request) { +func feedbackHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { data, err := ioutil.ReadAll(r.Body) if err != nil { panic(err) @@ -638,7 +669,8 @@ func feedbackHandler(w http.ResponseWriter, r *http.Request) { panic(fmt.Sprintf("feedbackHandler: body=%#v, err=%v", data, err)) } fmt.Printf("feedbackHandler: received request containg feedback %#v\n", feedback) - checkGormResult(GLOBAL_DB.Create(feedback)) + checkGormResult(GLOBAL_DB.WithContext(ctx).Create(feedback)) + GLOBAL_STATSD.Incr("hishtory.uninstall", []string{}, 1.0) } type loggedResponseData struct { @@ -660,7 +692,12 @@ func (r *loggingResponseWriter) WriteHeader(statusCode int) { r.ResponseWriter.WriteHeader(statusCode) } -func withLogging(h func(http.ResponseWriter, *http.Request)) http.Handler { +func getFunctionName(temp interface{}) string { + strs := strings.Split((runtime.FuncForPC(reflect.ValueOf(temp).Pointer()).Name()), ".") + return strs[len(strs)-1] +} + +func withLogging(h func(context.Context, http.ResponseWriter, *http.Request)) http.Handler { logFn := func(rw http.ResponseWriter, r *http.Request) { var responseData loggedResponseData lrw := loggingResponseWriter{ @@ -668,11 +705,20 @@ func withLogging(h func(http.ResponseWriter, *http.Request)) http.Handler { responseData: &responseData, } start := time.Now() + span, ctx := tracer.StartSpanFromContext( + context.Background(), + getFunctionName(h), + tracer.SpanType(ext.SpanTypeSQL), + tracer.ServiceName("hishtory-api"), + ) + defer span.Finish() - h(&lrw, r) + h(ctx, &lrw, r) duration := time.Since(start) fmt.Printf("%s %s %#v %s %s %s\n", getRemoteAddr(r), r.Method, r.RequestURI, getHishtoryVersion(r), duration.String(), byteCountToString(responseData.size)) + GLOBAL_STATSD.Distribution("hishtory.request_duration", float64(duration.Microseconds())/1_000, []string{"HANDLER=" + getFunctionName(h)}, 1.0) + GLOBAL_STATSD.Incr("hishtory.request", []string{}, 1.0) } return http.HandlerFunc(logFn) } @@ -690,35 +736,64 @@ func byteCountToString(b int) string { return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMG"[exp]) } -func cleanDatabase() error { - checkGormResult(GLOBAL_DB.Exec("DELETE FROM enc_history_entries WHERE read_count > 10")) - checkGormResult(GLOBAL_DB.Exec("DELETE FROM deletion_requests WHERE read_count > 100")) +func cleanDatabase(ctx context.Context) error { + checkGormResult(GLOBAL_DB.WithContext(ctx).Exec("DELETE FROM enc_history_entries WHERE read_count > 10")) + checkGormResult(GLOBAL_DB.WithContext(ctx).Exec("DELETE FROM deletion_requests WHERE read_count > 100")) // TODO(optimization): Clean the database by deleting entries for users that haven't been used in X amount of time return nil } func main() { - fmt.Println("Listening on localhost:8080") - http.Handle("/api/v1/submit", withLogging(apiSubmitHandler)) - http.Handle("/api/v1/get-dump-requests", withLogging(apiGetPendingDumpRequestsHandler)) - http.Handle("/api/v1/submit-dump", withLogging(apiSubmitDumpHandler)) - http.Handle("/api/v1/query", withLogging(apiQueryHandler)) - http.Handle("/api/v1/bootstrap", withLogging(apiBootstrapHandler)) - http.Handle("/api/v1/register", withLogging(apiRegisterHandler)) - http.Handle("/api/v1/banner", withLogging(apiBannerHandler)) - http.Handle("/api/v1/download", withLogging(apiDownloadHandler)) - http.Handle("/api/v1/trigger-cron", withLogging(triggerCronHandler)) - http.Handle("/api/v1/get-deletion-requests", withLogging(getDeletionRequestsHandler)) - http.Handle("/api/v1/add-deletion-request", withLogging(addDeletionRequestHandler)) - http.Handle("/api/v1/slsa-status", withLogging(slsaStatusHandler)) - http.Handle("/api/v1/feedback", withLogging(feedbackHandler)) - http.Handle("/healthcheck", withLogging(healthCheckHandler)) - http.Handle("/internal/api/v1/usage-stats", withLogging(usageStatsHandler)) - http.Handle("/internal/api/v1/stats", withLogging(statsHandler)) - if isTestEnvironment() { - http.Handle("/api/v1/wipe-db", withLogging(wipeDbHandler)) + if isProductionEnvironment() { + err := profiler.Start( + profiler.WithService("hishtory-api"), + profiler.WithVersion(ReleaseVersion), + profiler.WithAPIKey(os.Getenv("DD_API_KEY")), + profiler.WithUDS("/var/run/datadog/apm.socket"), + profiler.WithProfileTypes( + profiler.CPUProfile, + profiler.HeapProfile, + ), + ) + if err != nil { + fmt.Printf("Failed to start DataDog profiler: %v\n", err) + } + defer profiler.Stop() + tracer.Start( + tracer.WithRuntimeMetrics(), + tracer.WithService("hishtory-api"), + tracer.WithUDS("/var/run/datadog/apm.socket"), + ) + defer tracer.Stop() + ddStats, err := statsd.New("unix:///var/run/datadog/dsd.socket") + if err != nil { + fmt.Printf("Failed to start DataDog statsd: %v\n", err) + } + GLOBAL_STATSD = ddStats } - log.Fatal(http.ListenAndServe(":8080", nil)) + + mux := httptrace.NewServeMux() + mux.Handle("/api/v1/submit", withLogging(apiSubmitHandler)) + mux.Handle("/api/v1/get-dump-requests", withLogging(apiGetPendingDumpRequestsHandler)) + mux.Handle("/api/v1/submit-dump", withLogging(apiSubmitDumpHandler)) + mux.Handle("/api/v1/query", withLogging(apiQueryHandler)) + mux.Handle("/api/v1/bootstrap", withLogging(apiBootstrapHandler)) + mux.Handle("/api/v1/register", withLogging(apiRegisterHandler)) + mux.Handle("/api/v1/banner", withLogging(apiBannerHandler)) + mux.Handle("/api/v1/download", withLogging(apiDownloadHandler)) + mux.Handle("/api/v1/trigger-cron", withLogging(triggerCronHandler)) + mux.Handle("/api/v1/get-deletion-requests", withLogging(getDeletionRequestsHandler)) + mux.Handle("/api/v1/add-deletion-request", withLogging(addDeletionRequestHandler)) + mux.Handle("/api/v1/slsa-status", withLogging(slsaStatusHandler)) + mux.Handle("/api/v1/feedback", withLogging(feedbackHandler)) + mux.Handle("/healthcheck", withLogging(healthCheckHandler)) + mux.Handle("/internal/api/v1/usage-stats", withLogging(usageStatsHandler)) + mux.Handle("/internal/api/v1/stats", withLogging(statsHandler)) + if isTestEnvironment() { + mux.Handle("/api/v1/wipe-db", withLogging(wipeDbHandler)) + } + fmt.Println("Listening on localhost:8080") + log.Fatal(http.ListenAndServe(":8080", mux)) } func checkGormResult(result *gorm.DB) { diff --git a/backend/server/server_test.go b/backend/server/server_test.go index 6cdbdb8..bd9c6c8 100644 --- a/backend/server/server_test.go +++ b/backend/server/server_test.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "context" "encoding/json" "io/ioutil" "net/http" @@ -28,11 +29,11 @@ func TestESubmitThenQuery(t *testing.T) { otherUser := data.UserId("otherkey") otherDev := uuid.Must(uuid.NewRandom()).String() deviceReq := httptest.NewRequest(http.MethodGet, "/?device_id="+devId1+"&user_id="+userId, nil) - apiRegisterHandler(nil, deviceReq) + apiRegisterHandler(context.Background(), nil, deviceReq) deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+devId2+"&user_id="+userId, nil) - apiRegisterHandler(nil, deviceReq) + apiRegisterHandler(context.Background(), nil, deviceReq) deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+otherDev+"&user_id="+otherUser, nil) - apiRegisterHandler(nil, deviceReq) + apiRegisterHandler(context.Background(), nil, deviceReq) // Submit a few entries for different devices entry := testutils.MakeFakeHistoryEntry("ls ~/") @@ -41,12 +42,12 @@ func TestESubmitThenQuery(t *testing.T) { reqBody, err := json.Marshal([]shared.EncHistoryEntry{encEntry}) testutils.Check(t, err) submitReq := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody)) - apiSubmitHandler(nil, submitReq) + apiSubmitHandler(context.Background(), nil, submitReq) // Query for device id 1 w := httptest.NewRecorder() searchReq := httptest.NewRequest(http.MethodGet, "/?device_id="+devId1+"&user_id="+userId, nil) - apiQueryHandler(w, searchReq) + apiQueryHandler(context.Background(), w, searchReq) res := w.Result() defer res.Body.Close() respBody, err := ioutil.ReadAll(res.Body) @@ -75,7 +76,7 @@ func TestESubmitThenQuery(t *testing.T) { // Same for device id 2 w = httptest.NewRecorder() searchReq = httptest.NewRequest(http.MethodGet, "/?device_id="+devId2+"&user_id="+userId, nil) - apiQueryHandler(w, searchReq) + apiQueryHandler(context.Background(), w, searchReq) res = w.Result() defer res.Body.Close() respBody, err = ioutil.ReadAll(res.Body) @@ -103,7 +104,7 @@ func TestESubmitThenQuery(t *testing.T) { // Bootstrap handler should return 2 entries, one for each device w = httptest.NewRecorder() searchReq = httptest.NewRequest(http.MethodGet, "/?user_id="+data.UserId("key")+"&device_id="+devId1, nil) - apiBootstrapHandler(w, searchReq) + apiBootstrapHandler(context.Background(), w, searchReq) res = w.Result() defer res.Body.Close() respBody, err = ioutil.ReadAll(res.Body) @@ -126,17 +127,17 @@ func TestDumpRequestAndResponse(t *testing.T) { otherDev1 := uuid.Must(uuid.NewRandom()).String() otherDev2 := uuid.Must(uuid.NewRandom()).String() deviceReq := httptest.NewRequest(http.MethodGet, "/?device_id="+devId1+"&user_id="+userId, nil) - apiRegisterHandler(nil, deviceReq) + apiRegisterHandler(context.Background(), nil, deviceReq) deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+devId2+"&user_id="+userId, nil) - apiRegisterHandler(nil, deviceReq) + apiRegisterHandler(context.Background(), nil, deviceReq) deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+otherDev1+"&user_id="+otherUser, nil) - apiRegisterHandler(nil, deviceReq) + apiRegisterHandler(context.Background(), nil, deviceReq) deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+otherDev2+"&user_id="+otherUser, nil) - apiRegisterHandler(nil, deviceReq) + apiRegisterHandler(context.Background(), nil, deviceReq) // Query for dump requests, there should be one for userId w := httptest.NewRecorder() - apiGetPendingDumpRequestsHandler(w, httptest.NewRequest(http.MethodGet, "/?user_id="+userId+"&device_id="+devId1, nil)) + apiGetPendingDumpRequestsHandler(context.Background(), w, httptest.NewRequest(http.MethodGet, "/?user_id="+userId+"&device_id="+devId1, nil)) res := w.Result() defer res.Body.Close() respBody, err := ioutil.ReadAll(res.Body) @@ -156,7 +157,7 @@ func TestDumpRequestAndResponse(t *testing.T) { // And one for otherUser w = httptest.NewRecorder() - apiGetPendingDumpRequestsHandler(w, httptest.NewRequest(http.MethodGet, "/?user_id="+otherUser+"&device_id="+otherDev1, nil)) + apiGetPendingDumpRequestsHandler(context.Background(), w, httptest.NewRequest(http.MethodGet, "/?user_id="+otherUser+"&device_id="+otherDev1, nil)) res = w.Result() defer res.Body.Close() respBody, err = ioutil.ReadAll(res.Body) @@ -176,7 +177,7 @@ func TestDumpRequestAndResponse(t *testing.T) { // And none if we query for a user ID that doesn't exit w = httptest.NewRecorder() - apiGetPendingDumpRequestsHandler(w, httptest.NewRequest(http.MethodGet, "/?user_id=foo&device_id=bar", nil)) + apiGetPendingDumpRequestsHandler(context.Background(), w, httptest.NewRequest(http.MethodGet, "/?user_id=foo&device_id=bar", nil)) res = w.Result() defer res.Body.Close() respBody, err = ioutil.ReadAll(res.Body) @@ -187,7 +188,7 @@ func TestDumpRequestAndResponse(t *testing.T) { // And none for a missing user ID w = httptest.NewRecorder() - apiGetPendingDumpRequestsHandler(w, httptest.NewRequest(http.MethodGet, "/?user_id=%20&device_id=%20", nil)) + apiGetPendingDumpRequestsHandler(context.Background(), w, httptest.NewRequest(http.MethodGet, "/?user_id=%20&device_id=%20", nil)) res = w.Result() defer res.Body.Close() respBody, err = ioutil.ReadAll(res.Body) @@ -206,11 +207,11 @@ func TestDumpRequestAndResponse(t *testing.T) { reqBody, err := json.Marshal([]shared.EncHistoryEntry{entry1, entry2}) testutils.Check(t, err) submitReq := httptest.NewRequest(http.MethodPost, "/?user_id="+userId+"&requesting_device_id="+devId2+"&source_device_id="+devId1, bytes.NewReader(reqBody)) - apiSubmitDumpHandler(nil, submitReq) + apiSubmitDumpHandler(context.Background(), nil, submitReq) // Check that the dump request is no longer there for userId for either device ID w = httptest.NewRecorder() - apiGetPendingDumpRequestsHandler(w, httptest.NewRequest(http.MethodGet, "/?user_id="+userId+"&device_id="+devId1, nil)) + apiGetPendingDumpRequestsHandler(context.Background(), w, httptest.NewRequest(http.MethodGet, "/?user_id="+userId+"&device_id="+devId1, nil)) res = w.Result() defer res.Body.Close() respBody, err = ioutil.ReadAll(res.Body) @@ -220,7 +221,7 @@ func TestDumpRequestAndResponse(t *testing.T) { } w = httptest.NewRecorder() // The other user - apiGetPendingDumpRequestsHandler(w, httptest.NewRequest(http.MethodGet, "/?user_id="+userId+"&device_id="+devId2, nil)) + apiGetPendingDumpRequestsHandler(context.Background(), w, httptest.NewRequest(http.MethodGet, "/?user_id="+userId+"&device_id="+devId2, nil)) res = w.Result() defer res.Body.Close() respBody, err = ioutil.ReadAll(res.Body) @@ -231,7 +232,7 @@ func TestDumpRequestAndResponse(t *testing.T) { // But it is there for the other user w = httptest.NewRecorder() - apiGetPendingDumpRequestsHandler(w, httptest.NewRequest(http.MethodGet, "/?user_id="+otherUser+"&device_id="+otherDev1, nil)) + apiGetPendingDumpRequestsHandler(context.Background(), w, httptest.NewRequest(http.MethodGet, "/?user_id="+otherUser+"&device_id="+otherDev1, nil)) res = w.Result() defer res.Body.Close() respBody, err = ioutil.ReadAll(res.Body) @@ -252,7 +253,7 @@ func TestDumpRequestAndResponse(t *testing.T) { // And finally, query to ensure that the dumped entries are in the DB w = httptest.NewRecorder() searchReq := httptest.NewRequest(http.MethodGet, "/?device_id="+devId2+"&user_id="+userId, nil) - apiQueryHandler(w, searchReq) + apiQueryHandler(context.Background(), w, searchReq) res = w.Result() defer res.Body.Close() respBody, err = ioutil.ReadAll(res.Body) @@ -321,13 +322,13 @@ func TestDeletionRequests(t *testing.T) { otherDev1 := uuid.Must(uuid.NewRandom()).String() otherDev2 := uuid.Must(uuid.NewRandom()).String() deviceReq := httptest.NewRequest(http.MethodGet, "/?device_id="+devId1+"&user_id="+userId, nil) - apiRegisterHandler(nil, deviceReq) + apiRegisterHandler(context.Background(), nil, deviceReq) deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+devId2+"&user_id="+userId, nil) - apiRegisterHandler(nil, deviceReq) + apiRegisterHandler(context.Background(), nil, deviceReq) deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+otherDev1+"&user_id="+otherUser, nil) - apiRegisterHandler(nil, deviceReq) + apiRegisterHandler(context.Background(), nil, deviceReq) deviceReq = httptest.NewRequest(http.MethodGet, "/?device_id="+otherDev2+"&user_id="+otherUser, nil) - apiRegisterHandler(nil, deviceReq) + apiRegisterHandler(context.Background(), nil, deviceReq) // Add an entry for user1 entry1 := testutils.MakeFakeHistoryEntry("ls ~/") @@ -337,7 +338,7 @@ func TestDeletionRequests(t *testing.T) { reqBody, err := json.Marshal([]shared.EncHistoryEntry{encEntry}) testutils.Check(t, err) submitReq := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody)) - apiSubmitHandler(nil, submitReq) + apiSubmitHandler(context.Background(), nil, submitReq) // And another entry for user1 entry2 := testutils.MakeFakeHistoryEntry("ls /foo/bar") @@ -347,7 +348,7 @@ func TestDeletionRequests(t *testing.T) { reqBody, err = json.Marshal([]shared.EncHistoryEntry{encEntry}) testutils.Check(t, err) submitReq = httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody)) - apiSubmitHandler(nil, submitReq) + apiSubmitHandler(context.Background(), nil, submitReq) // And an entry for user2 that has the same timestamp as the previous entry entry3 := testutils.MakeFakeHistoryEntry("ls /foo/bar") @@ -358,12 +359,12 @@ func TestDeletionRequests(t *testing.T) { reqBody, err = json.Marshal([]shared.EncHistoryEntry{encEntry}) testutils.Check(t, err) submitReq = httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody)) - apiSubmitHandler(nil, submitReq) + apiSubmitHandler(context.Background(), nil, submitReq) // Query for device id 1 w := httptest.NewRecorder() searchReq := httptest.NewRequest(http.MethodGet, "/?device_id="+devId1+"&user_id="+userId, nil) - apiQueryHandler(w, searchReq) + apiQueryHandler(context.Background(), w, searchReq) res := w.Result() defer res.Body.Close() respBody, err := ioutil.ReadAll(res.Body) @@ -402,12 +403,12 @@ func TestDeletionRequests(t *testing.T) { reqBody, err = json.Marshal(delReq) testutils.Check(t, err) req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody)) - addDeletionRequestHandler(nil, req) + addDeletionRequestHandler(context.Background(), nil, req) // Query again for device id 1 and get a single result w = httptest.NewRecorder() searchReq = httptest.NewRequest(http.MethodGet, "/?device_id="+devId1+"&user_id="+userId, nil) - apiQueryHandler(w, searchReq) + apiQueryHandler(context.Background(), w, searchReq) res = w.Result() defer res.Body.Close() respBody, err = ioutil.ReadAll(res.Body) @@ -435,7 +436,7 @@ func TestDeletionRequests(t *testing.T) { // Query for user 2 w = httptest.NewRecorder() searchReq = httptest.NewRequest(http.MethodGet, "/?device_id="+otherDev1+"&user_id="+otherUser, nil) - apiQueryHandler(w, searchReq) + apiQueryHandler(context.Background(), w, searchReq) res = w.Result() defer res.Body.Close() respBody, err = ioutil.ReadAll(res.Body) @@ -463,7 +464,7 @@ func TestDeletionRequests(t *testing.T) { // Query for deletion requests w = httptest.NewRecorder() searchReq = httptest.NewRequest(http.MethodGet, "/?device_id="+devId1+"&user_id="+userId, nil) - getDeletionRequestsHandler(w, searchReq) + getDeletionRequestsHandler(context.Background(), w, searchReq) res = w.Result() defer res.Body.Close() respBody, err = ioutil.ReadAll(res.Body) diff --git a/go.mod b/go.mod index 788c506..faac109 100644 --- a/go.mod +++ b/go.mod @@ -35,6 +35,12 @@ require ( github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/DataDog/datadog-agent/pkg/obfuscate v0.40.1 // indirect + github.com/DataDog/datadog-go v4.8.3+incompatible // indirect + github.com/DataDog/datadog-go/v5 v5.1.1 // indirect + github.com/DataDog/gostackparse v0.5.0 // indirect + github.com/DataDog/sketches-go v1.4.1 // indirect + github.com/Microsoft/go-winio v0.6.0 // indirect github.com/PaesslerAG/gval v1.0.0 // indirect github.com/PaesslerAG/jsonpath v0.1.1 // indirect github.com/ThalesIgnite/crypto11 v1.2.5 // indirect @@ -88,6 +94,7 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cyberphone/json-canonicalization v0.0.0-20210823021906-dc406ceaf94b // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgraph-io/ristretto v0.1.0 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/docker/cli v20.10.20+incompatible // indirect github.com/docker/distribution v2.8.1+incompatible // indirect @@ -97,6 +104,7 @@ require ( github.com/dustin/go-humanize v1.0.0 // indirect github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1 // indirect github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect + github.com/felixge/fgprof v0.9.3 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/fullstorydev/grpcurl v1.8.7 // indirect github.com/ghodss/yaml v1.0.0 // indirect @@ -131,6 +139,7 @@ require ( github.com/google/go-github/v45 v45.2.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect github.com/google/trillian v1.5.0 // indirect github.com/googleapis/gnostic v0.5.5 // indirect github.com/gorilla/websocket v1.4.2 // indirect @@ -189,7 +198,9 @@ require ( github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect + github.com/philhofer/fwd v1.1.1 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pkg/profile v1.7.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.13.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect @@ -222,6 +233,7 @@ require ( github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613 // indirect github.com/thales-e-security/pool v0.0.2 // indirect github.com/theupdateframework/go-tuf v0.5.2-0.20220930112810-3890c1e7ace4 // indirect + github.com/tinylib/msgp v1.1.6 // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect github.com/tjfoc/gmsm v1.3.2 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect @@ -254,6 +266,8 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.23.0 // indirect + go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect + go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect golang.org/x/crypto v0.1.0 // indirect golang.org/x/exp v0.0.0-20220823124025-807a23277127 // indirect golang.org/x/mod v0.6.0 // indirect @@ -264,10 +278,12 @@ require ( golang.org/x/text v0.4.0 // indirect golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect golang.org/x/tools v0.1.12 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a // indirect google.golang.org/grpc v1.50.1 // indirect google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/DataDog/dd-trace-go.v1 v1.43.1 // indirect gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect @@ -275,6 +291,7 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + inet.af/netaddr v0.0.0-20220617031823-097006376321 // indirect k8s.io/api v0.23.5 // indirect k8s.io/apimachinery v0.23.5 // indirect k8s.io/client-go v0.23.5 // indirect diff --git a/go.sum b/go.sum index e74e074..395fd91 100644 --- a/go.sum +++ b/go.sum @@ -170,7 +170,21 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-agent/pkg/obfuscate v0.40.1 h1:2BL6Ek+TJdZudQdNdTz9XEpLCrN60aED/cKfQRTywVU= +github.com/DataDog/datadog-agent/pkg/obfuscate v0.40.1/go.mod h1:sqAOmqMk+eRSdoxm+ZVUhMzHCNVDAyO4e7oNj4mDwPU= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/datadog-go v4.8.2+incompatible h1:qbcKSx29aBLD+5QLvlQZlGmRMF/FfGqFLFev/1TDzRo= +github.com/DataDog/datadog-go v4.8.3+incompatible h1:fNGaYSuObuQb5nzeTQqowRAd9bpDIRRV4/gUtIBjh8Q= +github.com/DataDog/datadog-go v4.8.3+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/datadog-go/v5 v5.0.2 h1:UFtEe7662/Qojxkw1d6SboAeA0CPI3naKhVASwFn+04= +github.com/DataDog/datadog-go/v5 v5.0.2/go.mod h1:ZI9JFB4ewXbw1sBnF4sxsR2k1H3xjV+PUAOUsHvKpcU= +github.com/DataDog/datadog-go/v5 v5.1.0/go.mod h1:KhiYb2Badlv9/rofz+OznKoEF5XKTonWyhx5K83AP8E= +github.com/DataDog/datadog-go/v5 v5.1.1 h1:JLZ6s2K1pG2h9GkvEvMdEGqMDyVLEAccdX5TltWcLMU= +github.com/DataDog/datadog-go/v5 v5.1.1/go.mod h1:KhiYb2Badlv9/rofz+OznKoEF5XKTonWyhx5K83AP8E= +github.com/DataDog/gostackparse v0.5.0 h1:jb72P6GFHPHz2W0onsN51cS3FkaMDcjb0QzgxxA4gDk= +github.com/DataDog/gostackparse v0.5.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM= +github.com/DataDog/sketches-go v1.4.1 h1:j5G6as+9FASM2qC36lvpvQAj9qsv/jUs3FtO8CwZNAY= +github.com/DataDog/sketches-go v1.4.1/go.mod h1:xJIXldczJyyjnbDop7ZZcLxJdV3+7Kra7H1KMgpgkLk= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= @@ -194,7 +208,10 @@ github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugX github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= @@ -650,8 +667,11 @@ github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCF github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/depcheck-test/depcheck-test v0.0.0-20220607135614-199033aaa936 h1:foGzavPWwtoyBvjWyKJYDYsyzy+23iBV7NKTwdk+LRY= github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= +github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= +github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= @@ -691,6 +711,7 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3 github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -732,6 +753,8 @@ github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= +github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/flynn/go-docopt v0.0.0-20140912013429-f6dd2ebbb31e/go.mod h1:HyVoz1Mz5Co8TFO8EupIdlcpwShBmY98dkT2xeHkvEI= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -1059,7 +1082,10 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= @@ -1249,6 +1275,7 @@ github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmK github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -1715,6 +1742,8 @@ github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaF github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= +github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -1727,6 +1756,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= +github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -2037,6 +2068,8 @@ github.com/theupdateframework/go-tuf v0.5.2-0.20220930112810-3890c1e7ace4/go.mod github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= +github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw= +github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= @@ -2274,6 +2307,11 @@ go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE= +go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 h1:FyBZqvoA/jbNzuAWLQE2kG820zMAkcilx6BMjGbL/E4= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= gocloud.dev v0.19.0/go.mod h1:SmKwiR8YwIMMJvQBKLsC3fHNyMwXLw3PMDO+VVteJMI= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -2791,6 +2829,7 @@ golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82u golang.org/x/tools v0.0.0-20201001104356-43ebab892c4c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201014170642-d1624618ad65/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201028025901-8cd080b735b3/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -2831,6 +2870,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -3080,6 +3121,8 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/DataDog/dd-trace-go.v1 v1.43.1 h1:Dez4VzRQWAI5YXJRBx58BiC0gONGuW/oY4l8fWKzOXY= +gopkg.in/DataDog/dd-trace-go.v1 v1.43.1/go.mod h1:YL9g+nlUY7ByCffD5pDytAqy99GNbytRV0EBpKuldM4= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/statsd.v2 v2.0.0 h1:FXkZSCZIH17vLCO5sO2UucTHsH9pc+17F6pl3JVCwMc= @@ -3164,6 +3207,8 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= +inet.af/netaddr v0.0.0-20220617031823-097006376321 h1:B4dC8ySKTQXasnjDTMsoCMf1sQG4WsMej0WXaHxunmU= +inet.af/netaddr v0.0.0-20220617031823-097006376321/go.mod h1:OIezDfdzOgFhuw4HuWapWq2e9l0H9tK4F1j+ETRtF3k= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8=