diff --git a/cmd/admin-revoker/main.go b/cmd/admin-revoker/main.go index b2c45db0c..ca60e313b 100644 --- a/cmd/admin-revoker/main.go +++ b/cmd/admin-revoker/main.go @@ -167,7 +167,7 @@ func (r *revoker) revokeCertificate(ctx context.Context, certObj core.Certificat } func (r *revoker) revokeBySerial(ctx context.Context, serial string, reasonCode revocation.Reason, skipBlockKey bool) error { - certObj, err := sa.SelectPrecertificate(r.dbMap, serial) + certObj, err := sa.SelectPrecertificate(ctx, r.dbMap, serial) if err != nil { if db.IsNoRows(err) { return berrors.NotFoundError("precertificate with serial %q not found", serial) @@ -224,7 +224,7 @@ func (r *revoker) revokeSerialBatchFile(ctx context.Context, serialPath string, // Finding relevant accounts will be very slow because it does not use an index. func (r *revoker) clearEmailAddress(ctx context.Context, email string) error { r.log.AuditInfof("Scanning database for accounts with email addresses matching %q in order to clear the email addresses.", email) - regIDs, err := r.getRegIDsMatchingEmail(email) + regIDs, err := r.getRegIDsMatchingEmail(ctx, email) if err != nil { return err } @@ -233,7 +233,7 @@ func (r *revoker) clearEmailAddress(ctx context.Context, email string) error { failures := 0 for _, regID := range regIDs { - err := sa.ClearEmail(r.dbMap, ctx, regID, email) + err := sa.ClearEmail(ctx, r.dbMap, regID, email) if err != nil { // Log, but don't fail, because it took a long time to find the relevant registration IDs // and we don't want to have to redo that work. @@ -297,7 +297,7 @@ func (r *revoker) revokeByReg(ctx context.Context, regID int64, reasonCode revoc return fmt.Errorf("couldn't fetch registration: %w", err) } - certObjs, err := sa.SelectPrecertificates(r.dbMap, "WHERE registrationID = :regID", map[string]interface{}{"regID": regID}) + certObjs, err := sa.SelectPrecertificates(ctx, r.dbMap, "WHERE registrationID = :regID", map[string]interface{}{"regID": regID}) if err != nil { return err } @@ -372,7 +372,7 @@ func (r *revoker) revokeByPrivateKey(ctx context.Context, privateKey string) err return err } - matches, err := r.getCertsMatchingSPKIHash(spkiHash) + matches, err := r.getCertsMatchingSPKIHash(ctx, spkiHash) if err != nil { return err } @@ -408,9 +408,9 @@ func (r *revoker) revokeByPrivateKey(ctx context.Context, privateKey string) err return nil } -func (r *revoker) spkiHashInBlockedKeys(spkiHash []byte) (bool, error) { +func (r *revoker) spkiHashInBlockedKeys(ctx context.Context, spkiHash []byte) (bool, error) { var count int - err := r.dbMap.SelectOne(&count, "SELECT COUNT(*) as count FROM blockedKeys WHERE keyHash = ?", spkiHash) + err := r.dbMap.SelectOne(ctx, &count, "SELECT COUNT(*) as count FROM blockedKeys WHERE keyHash = ?", spkiHash) if err != nil { return false, err } @@ -421,9 +421,9 @@ func (r *revoker) spkiHashInBlockedKeys(spkiHash []byte) (bool, error) { return false, nil } -func (r *revoker) countCertsMatchingSPKIHash(spkiHash []byte) (int, error) { +func (r *revoker) countCertsMatchingSPKIHash(ctx context.Context, spkiHash []byte) (int, error) { var count int - err := r.dbMap.SelectOne(&count, "SELECT COUNT(*) as count FROM keyHashToSerial WHERE keyHash = ?", spkiHash) + err := r.dbMap.SelectOne(ctx, &count, "SELECT COUNT(*) as count FROM keyHashToSerial WHERE keyHash = ?", spkiHash) if err != nil { return 0, err } @@ -434,11 +434,11 @@ func (r *revoker) countCertsMatchingSPKIHash(spkiHash []byte) (int, error) { // contains the given email address. Since this uses a substring match, it is important // to subsequently parse the JSON list of addresses and look for exact matches. // Note: Since this does not use an index, it is very slow. -func (r *revoker) getRegIDsMatchingEmail(email string) ([]int64, error) { +func (r *revoker) getRegIDsMatchingEmail(ctx context.Context, email string) ([]int64, error) { // We use SQL `CONCAT` rather than interpolating with `+` or `%s` because we want to // use a `?` placeholder for the email, which prevents SQL injection. var regIDs []int64 - _, err := r.dbMap.Select(®IDs, "SELECT id FROM registrations WHERE contact LIKE CONCAT('%\"mailto:', ?, '\"%')", email) + _, err := r.dbMap.Select(ctx, ®IDs, "SELECT id FROM registrations WHERE contact LIKE CONCAT('%\"mailto:', ?, '\"%')", email) if err != nil { return nil, err } @@ -447,9 +447,9 @@ func (r *revoker) getRegIDsMatchingEmail(email string) ([]int64, error) { // TODO(#5899) Use an non-wrapped sql.Db client to iterate over results and // return them on a channel. -func (r *revoker) getCertsMatchingSPKIHash(spkiHash []byte) ([]string, error) { +func (r *revoker) getCertsMatchingSPKIHash(ctx context.Context, spkiHash []byte) ([]string, error) { var h []string - _, err := r.dbMap.Select(&h, "SELECT certSerial FROM keyHashToSerial WHERE keyHash = ?", spkiHash) + _, err := r.dbMap.Select(ctx, &h, "SELECT certSerial FROM keyHashToSerial WHERE keyHash = ?", spkiHash) if err != nil { if db.IsNoRows(err) { return nil, berrors.NotFoundError("no certificates with a matching SPKI hash were found") @@ -466,8 +466,8 @@ func (rc revocationCodes) Len() int { return len(rc) } func (rc revocationCodes) Less(i, j int) bool { return rc[i] < rc[j] } func (rc revocationCodes) Swap(i, j int) { rc[i], rc[j] = rc[j], rc[i] } -func privateKeyBlock(r *revoker, dryRun bool, comment string, count int, spkiHash []byte, keyPath string) error { - keyExists, err := r.spkiHashInBlockedKeys(spkiHash) +func privateKeyBlock(ctx context.Context, r *revoker, dryRun bool, comment string, count int, spkiHash []byte, keyPath string) error { + keyExists, err := r.spkiHashInBlockedKeys(ctx, spkiHash) if err != nil { return fmt.Errorf("while checking if the provided key already exists in the 'blockedKeys' table: %s", err) } @@ -640,12 +640,12 @@ func main() { spkiHash, err := getPublicKeySPKIHash(publicKey) cmd.FailOnError(err, "While obtaining the SPKI hash for the provided key") - count, err := r.countCertsMatchingSPKIHash(spkiHash) + count, err := r.countCertsMatchingSPKIHash(ctx, spkiHash) cmd.FailOnError(err, "While retrieving a count of certificates matching the provided key") r.log.AuditInfof("Found %d certificates matching the provided key", count) if command == "private-key-block" { - err := privateKeyBlock(r, *dryRun, *comment, count, spkiHash, keyPath) + err := privateKeyBlock(ctx, r, *dryRun, *comment, count, spkiHash, keyPath) cmd.FailOnError(err, "") } diff --git a/cmd/admin-revoker/main_test.go b/cmd/admin-revoker/main_test.go index 3c2b9e027..0fd7a92bc 100644 --- a/cmd/admin-revoker/main_test.go +++ b/cmd/admin-revoker/main_test.go @@ -93,7 +93,10 @@ func TestRevokeIncidentTableSerials(t *testing.T) { test.Assert(t, len(testCtx.log.GetAllMatching("No serials found in incident table")) > 0, "Expected log output not found") testCtx.log.Clear() - _, err = testIncidentsDbMap.Exec( + ctx := context.Background() + + _, err = testIncidentsDbMap.ExecContext( + ctx, fmt.Sprintf("INSERT INTO incident_foo (%s) VALUES ('%s', %d, %d, '%s')", "serial, registrationID, orderID, lastNoticeSent", core.SerialToString(entries[0].serial), @@ -104,14 +107,14 @@ func TestRevokeIncidentTableSerials(t *testing.T) { ) test.AssertNotError(t, err, "while inserting row into incident table") - err = testCtx.revoker.revokeIncidentTableSerials(context.Background(), "incident_foo", 0, 1) + err = testCtx.revoker.revokeIncidentTableSerials(ctx, "incident_foo", 0, 1) test.AssertNotError(t, err, "revokeIncidentTableSerials failed") // Ensure that a populated incident table results in the expected log output. test.AssertNotError(t, err, "revokeIncidentTableSerials failed") test.Assert(t, len(testCtx.log.GetAllMatching("No serials found in incident table")) <= 0, "Expected log output not found") - status, err := testCtx.ssa.GetCertificateStatus(context.Background(), &sapb.Serial{Serial: core.SerialToString(entries[0].serial)}) + status, err := testCtx.ssa.GetCertificateStatus(ctx, &sapb.Serial{Serial: core.SerialToString(entries[0].serial)}) test.AssertNotError(t, err, "failed to retrieve certificate status") test.AssertEquals(t, core.OCSPStatus(status.Status), core.OCSPStatusRevoked) } @@ -143,9 +146,11 @@ func TestBlockAndRevokeByPrivateKey(t *testing.T) { spkiHash, err := getPublicKeySPKIHash(&duplicateEntry.testKey.PublicKey) test.AssertNotError(t, err, "Failed to get SPKI hash for dupe.") + ctx := context.Background() + // Ensure that the SPKI hash hasn't already been added to the blockedKeys // table. - keyExists, err := testCtx.revoker.spkiHashInBlockedKeys(spkiHash) + keyExists, err := testCtx.revoker.spkiHashInBlockedKeys(ctx, spkiHash) test.AssertNotError(t, err, "countCertsMatchingSPKIHash for dupe failed") test.Assert(t, !keyExists, "SPKI hash should not be in blockedKeys") @@ -155,19 +160,19 @@ func TestBlockAndRevokeByPrivateKey(t *testing.T) { switch e.names[0] { case uniqueEntries[0].names[0]: // example-1337.com - count, err := testCtx.revoker.countCertsMatchingSPKIHash(e.spkiHash) + count, err := testCtx.revoker.countCertsMatchingSPKIHash(ctx, e.spkiHash) test.AssertNotError(t, err, "countCertsMatchingSPKIHash for entry failed") test.AssertEquals(t, count, 2) case uniqueEntries[1].names[0]: // example-1338.com - count, err := testCtx.revoker.countCertsMatchingSPKIHash(e.spkiHash) + count, err := testCtx.revoker.countCertsMatchingSPKIHash(ctx, e.spkiHash) test.AssertNotError(t, err, "countCertsMatchingSPKIHash for entry failed") test.AssertEquals(t, count, 1) case uniqueEntries[2].names[0]: // example-1339.com - count, err := testCtx.revoker.countCertsMatchingSPKIHash(e.spkiHash) + count, err := testCtx.revoker.countCertsMatchingSPKIHash(ctx, e.spkiHash) test.AssertNotError(t, err, "countCertsMatchingSPKIHash for entry failed") test.AssertEquals(t, count, 1) } @@ -184,7 +189,7 @@ func TestBlockAndRevokeByPrivateKey(t *testing.T) { test.AssertNotError(t, err, "While attempting to revoke certificates for the provided key") // Ensure that the key is not blocked, yet. - keyExists, err = testCtx.revoker.spkiHashInBlockedKeys(spkiHash) + keyExists, err = testCtx.revoker.spkiHashInBlockedKeys(ctx, spkiHash) test.AssertNotError(t, err, "countCertsMatchingSPKIHash for dupe failed") test.Assert(t, !keyExists, "SPKI hash should not be in blockedKeys") @@ -193,7 +198,7 @@ func TestBlockAndRevokeByPrivateKey(t *testing.T) { test.AssertNotError(t, err, "While attempting to block issuance for the provided key") // Ensure that the key is now blocked. - keyExists, err = testCtx.revoker.spkiHashInBlockedKeys(spkiHash) + keyExists, err = testCtx.revoker.spkiHashInBlockedKeys(ctx, spkiHash) test.AssertNotError(t, err, "countCertsMatchingSPKIHash for dupe failed") test.Assert(t, keyExists, "SPKI hash should not be in blockedKeys") @@ -203,6 +208,7 @@ func TestBlockAndRevokeByPrivateKey(t *testing.T) { } func TestPrivateKeyBlock(t *testing.T) { + ctx := context.Background() testCtx := setup(t) defer testCtx.cleanUp() @@ -231,35 +237,35 @@ func TestPrivateKeyBlock(t *testing.T) { // Query the 'keyHashToSerial' table for certificates with a matching SPKI // hash. We expect that since this key was re-used we'll find 2 matches. - count, err := testCtx.revoker.countCertsMatchingSPKIHash(duplicateKeySPKI) + count, err := testCtx.revoker.countCertsMatchingSPKIHash(ctx, duplicateKeySPKI) test.AssertNotError(t, err, "countCertsMatchingSPKIHash for dupe failed") test.AssertEquals(t, count, 2) // With dryRun=true this should not block the key. - err = privateKeyBlock(&testCtx.revoker, true, "", count, duplicateKeySPKI, duplicateKeyFile.Name()) + err = privateKeyBlock(ctx, &testCtx.revoker, true, "", count, duplicateKeySPKI, duplicateKeyFile.Name()) test.AssertNotError(t, err, "While attempting to block issuance for the provided key") // Ensure that the key is not blocked, yet. - keyExists, err := testCtx.revoker.spkiHashInBlockedKeys(duplicateKeySPKI) + keyExists, err := testCtx.revoker.spkiHashInBlockedKeys(ctx, duplicateKeySPKI) test.AssertNotError(t, err, "countCertsMatchingSPKIHash for dupe failed") test.Assert(t, !keyExists, "SPKI hash should not be in blockedKeys") // With dryRun=false this should block the key. comment := "key blocked as part of test" - err = privateKeyBlock(&testCtx.revoker, false, comment, count, duplicateKeySPKI, duplicateKeyFile.Name()) + err = privateKeyBlock(ctx, &testCtx.revoker, false, comment, count, duplicateKeySPKI, duplicateKeyFile.Name()) test.AssertNotError(t, err, "While attempting to block issuance for the provided key") // With dryRun=false this should result in an error as the key is already blocked. - err = privateKeyBlock(&testCtx.revoker, false, "", count, duplicateKeySPKI, duplicateKeyFile.Name()) + err = privateKeyBlock(ctx, &testCtx.revoker, false, "", count, duplicateKeySPKI, duplicateKeyFile.Name()) test.AssertError(t, err, "Attempting to block a key which is already blocked should have failed.") // Ensure that the key is now blocked. - keyExists, err = testCtx.revoker.spkiHashInBlockedKeys(duplicateKeySPKI) + keyExists, err = testCtx.revoker.spkiHashInBlockedKeys(ctx, duplicateKeySPKI) test.AssertNotError(t, err, "countCertsMatchingSPKIHash for dupe failed") test.Assert(t, keyExists, "SPKI hash should not be in blockedKeys") // Ensure that the comment was set as expected - commentFromDB, err := testCtx.dbMap.SelectStr("SELECT comment from blockedKeys WHERE keyHash = ?", duplicateKeySPKI) + commentFromDB, err := testCtx.dbMap.SelectStr(ctx, "SELECT comment from blockedKeys WHERE keyHash = ?", duplicateKeySPKI) test.AssertNotError(t, err, "Failed to get comment from database") u, err := user.Current() test.AssertNotError(t, err, "Failed to get current user") @@ -268,6 +274,7 @@ func TestPrivateKeyBlock(t *testing.T) { } func TestPrivateKeyRevoke(t *testing.T) { + ctx := context.Background() testCtx := setup(t) defer testCtx.cleanUp() @@ -296,7 +303,7 @@ func TestPrivateKeyRevoke(t *testing.T) { // Query the 'keyHashToSerial' table for certificates with a matching SPKI // hash. We expect that since this key was re-used we'll find 2 matches. - count, err := testCtx.revoker.countCertsMatchingSPKIHash(duplicateKeySPKI) + count, err := testCtx.revoker.countCertsMatchingSPKIHash(ctx, duplicateKeySPKI) test.AssertNotError(t, err, "countCertsMatchingSPKIHash for dupe failed") test.AssertEquals(t, count, 2) @@ -305,7 +312,7 @@ func TestPrivateKeyRevoke(t *testing.T) { test.AssertNotError(t, err, "While attempting to block issuance for the provided key") // Ensure that the key is not blocked, yet. - keyExists, err := testCtx.revoker.spkiHashInBlockedKeys(duplicateKeySPKI) + keyExists, err := testCtx.revoker.spkiHashInBlockedKeys(ctx, duplicateKeySPKI) test.AssertNotError(t, err, "spkiHashInBlockedKeys failed for key that shouldn't be blocked yet") test.Assert(t, !keyExists, "SPKI hash should not be in blockedKeys") @@ -315,12 +322,12 @@ func TestPrivateKeyRevoke(t *testing.T) { test.AssertNotError(t, err, "While attempting to block issuance for the provided key") // Ensure that the key is now blocked. - keyExists, err = testCtx.revoker.spkiHashInBlockedKeys(duplicateKeySPKI) + keyExists, err = testCtx.revoker.spkiHashInBlockedKeys(ctx, duplicateKeySPKI) test.AssertNotError(t, err, "spkiHashInBlockedKeys failed for key that should now be blocked") test.Assert(t, keyExists, "SPKI hash should not be in blockedKeys") // Ensure that the comment was set as expected - commentFromDB, err := testCtx.dbMap.SelectStr("SELECT comment from blockedKeys WHERE keyHash = ?", duplicateKeySPKI) + commentFromDB, err := testCtx.dbMap.SelectStr(ctx, "SELECT comment from blockedKeys WHERE keyHash = ?", duplicateKeySPKI) test.AssertNotError(t, err, "Failed to get comment from database") u, err := user.Current() test.AssertNotError(t, err, "Failed to get current user") diff --git a/cmd/bad-key-revoker/main.go b/cmd/bad-key-revoker/main.go index 8fc67e0c7..052e46819 100644 --- a/cmd/bad-key-revoker/main.go +++ b/cmd/bad-key-revoker/main.go @@ -82,9 +82,10 @@ func (ubk uncheckedBlockedKey) String() string { ubk.RevokedBy, ubk.KeyHash) } -func (bkr *badKeyRevoker) countUncheckedKeys() (int, error) { +func (bkr *badKeyRevoker) countUncheckedKeys(ctx context.Context) (int, error) { var count int err := bkr.dbMap.SelectOne( + ctx, &count, `SELECT COUNT(*) FROM (SELECT 1 FROM blockedKeys @@ -95,9 +96,10 @@ func (bkr *badKeyRevoker) countUncheckedKeys() (int, error) { return count, err } -func (bkr *badKeyRevoker) selectUncheckedKey() (uncheckedBlockedKey, error) { +func (bkr *badKeyRevoker) selectUncheckedKey(ctx context.Context) (uncheckedBlockedKey, error) { var row uncheckedBlockedKey err := bkr.dbMap.SelectOne( + ctx, &row, `SELECT keyHash, revokedBy FROM blockedKeys @@ -125,7 +127,7 @@ func (uc unrevokedCertificate) String() string { // findUnrevoked looks for all unexpired, currently valid certificates which have a specific SPKI hash, // by looking first at the keyHashToSerial table and then the certificateStatus and certificates tables. // If the number of certificates it finds is larger than bkr.maxRevocations it'll error out. -func (bkr *badKeyRevoker) findUnrevoked(unchecked uncheckedBlockedKey) ([]unrevokedCertificate, error) { +func (bkr *badKeyRevoker) findUnrevoked(ctx context.Context, unchecked uncheckedBlockedKey) ([]unrevokedCertificate, error) { var unrevokedCerts []unrevokedCertificate initialID := 0 for { @@ -134,6 +136,7 @@ func (bkr *badKeyRevoker) findUnrevoked(unchecked uncheckedBlockedKey) ([]unrevo CertSerial string } _, err := bkr.dbMap.Select( + ctx, &batch, "SELECT id, certSerial FROM keyHashToSerial WHERE keyHash = ? AND id > ? AND certNotAfter > ? ORDER BY id LIMIT ?", unchecked.KeyHash, @@ -155,6 +158,7 @@ func (bkr *badKeyRevoker) findUnrevoked(unchecked uncheckedBlockedKey) ([]unrevo // possible we could get multiple results for a single serial number, but they // would be duplicates. err = bkr.dbMap.SelectOne( + ctx, &unrevokedCert, `SELECT cs.id, cs.serial, c.registrationID, c.der, cs.status, cs.isExpired FROM certificateStatus AS cs @@ -181,19 +185,19 @@ func (bkr *badKeyRevoker) findUnrevoked(unchecked uncheckedBlockedKey) ([]unrevo // markRowChecked updates a row in the blockedKeys table to mark a keyHash // as having been checked for extant unrevoked certificates. -func (bkr *badKeyRevoker) markRowChecked(unchecked uncheckedBlockedKey) error { - _, err := bkr.dbMap.Exec("UPDATE blockedKeys SET extantCertificatesChecked = true WHERE keyHash = ?", unchecked.KeyHash) +func (bkr *badKeyRevoker) markRowChecked(ctx context.Context, unchecked uncheckedBlockedKey) error { + _, err := bkr.dbMap.ExecContext(ctx, "UPDATE blockedKeys SET extantCertificatesChecked = true WHERE keyHash = ?", unchecked.KeyHash) return err } // resolveContacts builds a map of id -> email addresses -func (bkr *badKeyRevoker) resolveContacts(ids []int64) (map[int64][]string, error) { +func (bkr *badKeyRevoker) resolveContacts(ctx context.Context, ids []int64) (map[int64][]string, error) { idToEmail := map[int64][]string{} for _, id := range ids { var emails struct { Contact []string } - err := bkr.dbMap.SelectOne(&emails, "SELECT contact FROM registrations WHERE id = ?", id) + err := bkr.dbMap.SelectOne(ctx, &emails, "SELECT contact FROM registrations WHERE id = ?", id) if err != nil { // ErrNoRows is not acceptable here since there should always be a // row for the registration, even if there are no contacts @@ -289,9 +293,9 @@ func (bkr *badKeyRevoker) revokeCerts(revokerEmails []string, emailToCerts map[s // invoke processes a single key in the blockedKeys table and returns whether // there were any rows to process or not. -func (bkr *badKeyRevoker) invoke() (bool, error) { +func (bkr *badKeyRevoker) invoke(ctx context.Context) (bool, error) { // Gather a count of rows to be processed. - uncheckedCount, err := bkr.countUncheckedKeys() + uncheckedCount, err := bkr.countUncheckedKeys(ctx) if err != nil { return false, err } @@ -307,7 +311,7 @@ func (bkr *badKeyRevoker) invoke() (bool, error) { } // select a row to process - unchecked, err := bkr.selectUncheckedKey() + unchecked, err := bkr.selectUncheckedKey(ctx) if err != nil { if db.IsNoRows(err) { return true, nil @@ -317,7 +321,7 @@ func (bkr *badKeyRevoker) invoke() (bool, error) { bkr.logger.AuditInfo(fmt.Sprintf("found unchecked block key to work on: %s", unchecked)) // select all unrevoked, unexpired serials associated with the blocked key hash - unrevokedCerts, err := bkr.findUnrevoked(unchecked) + unrevokedCerts, err := bkr.findUnrevoked(ctx, unchecked) if err != nil { bkr.logger.AuditInfo(fmt.Sprintf("finding unrevoked certificates related to %s: %s", unchecked, err)) @@ -326,7 +330,7 @@ func (bkr *badKeyRevoker) invoke() (bool, error) { if len(unrevokedCerts) == 0 { bkr.logger.AuditInfo(fmt.Sprintf("found no certificates that need revoking related to %s, marking row as checked", unchecked)) // mark row as checked - err = bkr.markRowChecked(unchecked) + err = bkr.markRowChecked(ctx, unchecked) if err != nil { return false, err } @@ -352,7 +356,7 @@ func (bkr *badKeyRevoker) invoke() (bool, error) { ids = append(ids, unchecked.RevokedBy) } // get contact addresses for the list of IDs - idToEmails, err := bkr.resolveContacts(ids) + idToEmails, err := bkr.resolveContacts(ctx, ids) if err != nil { return false, err } @@ -377,7 +381,7 @@ func (bkr *badKeyRevoker) invoke() (bool, error) { } // mark the key as checked - err = bkr.markRowChecked(unchecked) + err = bkr.markRowChecked(ctx, unchecked) if err != nil { return false, err } @@ -523,7 +527,7 @@ func main() { // Run bad-key-revoker in a loop. Backoff if no work or errors. for { - noWork, err := bkr.invoke() + noWork, err := bkr.invoke(context.Background()) if err != nil { keysProcessed.WithLabelValues("error").Inc() logger.AuditErrf("failed to process blockedKeys row: %s", err) diff --git a/cmd/bad-key-revoker/main_test.go b/cmd/bad-key-revoker/main_test.go index 44f2def77..ab654ce32 100644 --- a/cmd/bad-key-revoker/main_test.go +++ b/cmd/bad-key-revoker/main_test.go @@ -34,7 +34,7 @@ func randHash(t *testing.T) []byte { func insertBlockedRow(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, hash []byte, by int64, checked bool) { t.Helper() - _, err := dbMap.Exec(`INSERT INTO blockedKeys + _, err := dbMap.ExecContext(context.Background(), `INSERT INTO blockedKeys (keyHash, added, source, revokedBy, extantCertificatesChecked) VALUES (?, ?, ?, ?, ?)`, @@ -48,6 +48,8 @@ func insertBlockedRow(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, hash [ } func TestSelectUncheckedRows(t *testing.T) { + ctx := context.Background() + dbMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms) test.AssertNotError(t, err, "failed setting up db client") defer test.ResetBoulderTestDatabase(t)() @@ -62,18 +64,18 @@ func TestSelectUncheckedRows(t *testing.T) { hashA, hashB, hashC := randHash(t), randHash(t), randHash(t) insertBlockedRow(t, dbMap, fc, hashA, 1, true) - count, err := bkr.countUncheckedKeys() + count, err := bkr.countUncheckedKeys(ctx) test.AssertNotError(t, err, "countUncheckedKeys failed") test.AssertEquals(t, count, 0) - _, err = bkr.selectUncheckedKey() + _, err = bkr.selectUncheckedKey(ctx) test.AssertError(t, err, "selectUncheckedKey didn't fail with no rows to process") test.Assert(t, db.IsNoRows(err), "returned error is not sql.ErrNoRows") insertBlockedRow(t, dbMap, fc, hashB, 1, false) insertBlockedRow(t, dbMap, fc, hashC, 1, false) - count, err = bkr.countUncheckedKeys() + count, err = bkr.countUncheckedKeys(ctx) test.AssertNotError(t, err, "countUncheckedKeys failed") test.AssertEquals(t, count, 2) - row, err := bkr.selectUncheckedKey() + row, err := bkr.selectUncheckedKey(ctx) test.AssertNotError(t, err, "selectUncheckKey failed") test.AssertByteEquals(t, row.KeyHash, hashB) test.AssertEquals(t, row.RevokedBy, int64(1)) @@ -92,7 +94,8 @@ func insertRegistration(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, addr } contactStr = fmt.Sprintf("[%s]", strings.Join(contacts, ",")) } - res, err := dbMap.Exec( + res, err := dbMap.ExecContext( + context.Background(), "INSERT INTO registrations (jwk, jwk_sha256, contact, agreement, initialIP, createdAt, status, LockCol) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", []byte{}, fmt.Sprintf("%x", jwkHash), @@ -124,13 +127,15 @@ func insertGoodCert(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, keyHash func insertCert(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, keyHash []byte, serial string, regID int64, expiredStatus ExpiredStatus, status core.OCSPStatus) { t.Helper() + ctx := context.Background() expiresOffset := 0 * time.Second if !expiredStatus { expiresOffset = 90*24*time.Hour - 1*time.Second // 90 days exclusive } - _, err := dbMap.Exec( + _, err := dbMap.ExecContext( + ctx, `INSERT IGNORE INTO keyHashToSerial (keyHash, certNotAfter, certSerial) VALUES (?, ?, ?)`, @@ -140,7 +145,8 @@ func insertCert(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, keyHash []by ) test.AssertNotError(t, err, "failed to insert test keyHashToSerial row") - _, err = dbMap.Exec( + _, err = dbMap.ExecContext( + ctx, "INSERT INTO certificateStatus (serial, status, isExpired, ocspLastUpdated, revokedDate, revokedReason, lastExpirationNagSent) VALUES (?, ?, ?, ?, ?, ?, ?)", serial, status, @@ -152,7 +158,8 @@ func insertCert(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, keyHash []by ) test.AssertNotError(t, err, "failed to insert test certificateStatus row") - _, err = dbMap.Exec( + _, err = dbMap.ExecContext( + ctx, "INSERT INTO precertificates (serial, registrationID, der, issued, expires) VALUES (?, ?, ?, ?, ?)", serial, regID, @@ -162,7 +169,8 @@ func insertCert(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, keyHash []by ) test.AssertNotError(t, err, "failed to insert test certificateStatus row") - _, err = dbMap.Exec( + _, err = dbMap.ExecContext( + ctx, "INSERT INTO certificates (serial, registrationID, der, digest, issued, expires) VALUES (?, ?, ?, ?, ?, ?)", serial, regID, @@ -178,6 +186,8 @@ func insertCert(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, keyHash []by // does not have a corresponding entry in the certificateStatus and // precertificates table. func TestFindUnrevokedNoRows(t *testing.T) { + ctx := context.Background() + dbMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms) test.AssertNotError(t, err, "failed setting up db client") defer test.ResetBoulderTestDatabase(t)() @@ -185,7 +195,8 @@ func TestFindUnrevokedNoRows(t *testing.T) { fc := clock.NewFake() hashA := randHash(t) - _, err = dbMap.Exec( + _, err = dbMap.ExecContext( + ctx, "INSERT INTO keyHashToSerial (keyHash, certNotAfter, certSerial) VALUES (?, ?, ?)", hashA, fc.Now().Add(90*24*time.Hour-1*time.Second), // 90 days exclusive @@ -194,11 +205,13 @@ func TestFindUnrevokedNoRows(t *testing.T) { test.AssertNotError(t, err, "failed to insert test keyHashToSerial row") bkr := &badKeyRevoker{dbMap: dbMap, serialBatchSize: 1, maxRevocations: 10, clk: fc} - _, err = bkr.findUnrevoked(uncheckedBlockedKey{KeyHash: hashA}) + _, err = bkr.findUnrevoked(ctx, uncheckedBlockedKey{KeyHash: hashA}) test.Assert(t, db.IsNoRows(err), "expected NoRows error") } func TestFindUnrevoked(t *testing.T) { + ctx := context.Background() + dbMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms) test.AssertNotError(t, err, "failed setting up db client") defer test.ResetBoulderTestDatabase(t)() @@ -219,7 +232,7 @@ func TestFindUnrevoked(t *testing.T) { // insert revoked insertCert(t, dbMap, fc, hashA, "dd", regID, Unexpired, Revoked) - rows, err := bkr.findUnrevoked(uncheckedBlockedKey{KeyHash: hashA}) + rows, err := bkr.findUnrevoked(ctx, uncheckedBlockedKey{KeyHash: hashA}) test.AssertNotError(t, err, "findUnrevoked failed") test.AssertEquals(t, len(rows), 1) test.AssertEquals(t, rows[0].Serial, "ff") @@ -227,7 +240,7 @@ func TestFindUnrevoked(t *testing.T) { test.AssertByteEquals(t, rows[0].DER, []byte{1, 2, 3}) bkr.maxRevocations = 0 - _, err = bkr.findUnrevoked(uncheckedBlockedKey{KeyHash: hashA}) + _, err = bkr.findUnrevoked(ctx, uncheckedBlockedKey{KeyHash: hashA}) test.AssertError(t, err, "findUnrevoked didn't fail with 0 maxRevocations") test.AssertEquals(t, err.Error(), fmt.Sprintf("too many certificates to revoke associated with %x: got 1, max 0", hashA)) } @@ -246,7 +259,7 @@ func TestResolveContacts(t *testing.T) { regIDC := insertRegistration(t, dbMap, fc, "example.com") regIDD := insertRegistration(t, dbMap, fc, "example-2.com") - idToEmail, err := bkr.resolveContacts([]int64{regIDA, regIDB, regIDC, regIDD}) + idToEmail, err := bkr.resolveContacts(context.Background(), []int64{regIDA, regIDB, regIDC, regIDD}) test.AssertNotError(t, err, "resolveContacts failed") test.AssertDeepEquals(t, idToEmail, map[int64][]string{ regIDA: {""}, @@ -308,6 +321,8 @@ func TestRevokeCerts(t *testing.T) { } func TestCertificateAbsent(t *testing.T) { + ctx := context.Background() + dbMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms) test.AssertNotError(t, err, "failed setting up db client") defer test.ResetBoulderTestDatabase(t)() @@ -321,7 +336,8 @@ func TestCertificateAbsent(t *testing.T) { // Add an entry to keyHashToSerial but not to certificateStatus or certificate // status, and expect an error. - _, err = dbMap.Exec( + _, err = dbMap.ExecContext( + ctx, "INSERT INTO keyHashToSerial (keyHash, certNotAfter, certSerial) VALUES (?, ?, ?)", hashA, fc.Now().Add(90*24*time.Hour-1*time.Second), // 90 days exclusive @@ -340,11 +356,13 @@ func TestCertificateAbsent(t *testing.T) { logger: blog.NewMock(), clk: fc, } - _, err = bkr.invoke() + _, err = bkr.invoke(ctx) test.AssertError(t, err, "expected error when row in keyHashToSerial didn't have a matching cert") } func TestInvoke(t *testing.T) { + ctx := context.Background() + dbMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms) test.AssertNotError(t, err, "failed setting up db client") defer test.ResetBoulderTestDatabase(t)() @@ -377,7 +395,7 @@ func TestInvoke(t *testing.T) { insertGoodCert(t, dbMap, fc, hashA, "dd", regIDC) insertGoodCert(t, dbMap, fc, hashA, "cc", regIDD) - noWork, err := bkr.invoke() + noWork, err := bkr.invoke(ctx) test.AssertNotError(t, err, "invoke failed") test.AssertEquals(t, noWork, false) test.AssertEquals(t, mr.revoked, 4) @@ -388,7 +406,7 @@ func TestInvoke(t *testing.T) { var checked struct { ExtantCertificatesChecked bool } - err = dbMap.SelectOne(&checked, "SELECT extantCertificatesChecked FROM blockedKeys WHERE keyHash = ?", hashA) + err = dbMap.SelectOne(ctx, &checked, "SELECT extantCertificatesChecked FROM blockedKeys WHERE keyHash = ?", hashA) test.AssertNotError(t, err, "failed to select row from blockedKeys") test.AssertEquals(t, checked.ExtantCertificatesChecked, true) @@ -397,16 +415,16 @@ func TestInvoke(t *testing.T) { insertBlockedRow(t, dbMap, fc, hashB, regIDC, false) insertCert(t, dbMap, fc, hashB, "bb", regIDA, Expired, Revoked) - noWork, err = bkr.invoke() + noWork, err = bkr.invoke(ctx) test.AssertNotError(t, err, "invoke failed") test.AssertEquals(t, noWork, false) checked.ExtantCertificatesChecked = false - err = dbMap.SelectOne(&checked, "SELECT extantCertificatesChecked FROM blockedKeys WHERE keyHash = ?", hashB) + err = dbMap.SelectOne(ctx, &checked, "SELECT extantCertificatesChecked FROM blockedKeys WHERE keyHash = ?", hashB) test.AssertNotError(t, err, "failed to select row from blockedKeys") test.AssertEquals(t, checked.ExtantCertificatesChecked, true) - noWork, err = bkr.invoke() + noWork, err = bkr.invoke(ctx) test.AssertNotError(t, err, "invoke failed") test.AssertEquals(t, noWork, true) } @@ -450,7 +468,7 @@ func TestInvokeRevokerHasNoExtantCerts(t *testing.T) { insertGoodCert(t, dbMap, fc, hashA, "cc", regIDC) insertGoodCert(t, dbMap, fc, hashA, "bb", regIDC) - noWork, err := bkr.invoke() + noWork, err := bkr.invoke(context.Background()) test.AssertNotError(t, err, "invoke failed") test.AssertEquals(t, noWork, false) test.AssertEquals(t, mr.revoked, 4) diff --git a/cmd/cert-checker/main.go b/cmd/cert-checker/main.go index 1dbc8fadf..eff4c8f41 100644 --- a/cmd/cert-checker/main.go +++ b/cmd/cert-checker/main.go @@ -82,12 +82,12 @@ type reportEntry struct { Problems []string `json:"problems,omitempty"` } -// certDB is an interface collecting the gorp.saDbMap functions that the various +// certDB is an interface collecting the borp.DbMap functions that the various // parts of cert-checker rely on. Using this adapter shim allows tests to swap // out the saDbMap implementation. type certDB interface { - Select(i interface{}, query string, args ...interface{}) ([]interface{}, error) - SelectNullInt(query string, args ...interface{}) (sql.NullInt64, error) + Select(ctx context.Context, i interface{}, query string, args ...interface{}) ([]interface{}, error) + SelectNullInt(ctx context.Context, query string, args ...interface{}) (sql.NullInt64, error) } type certChecker struct { @@ -125,7 +125,7 @@ func newChecker(saDbMap certDB, } } -func (c *certChecker) getCerts(unexpiredOnly bool) error { +func (c *certChecker) getCerts(ctx context.Context, unexpiredOnly bool) error { c.issuedReport.end = c.clock.Now() c.issuedReport.begin = c.issuedReport.end.Add(-c.checkPeriod) @@ -139,6 +139,7 @@ func (c *certChecker) getCerts(unexpiredOnly bool) error { var retries int for { sni, err = c.dbMap.SelectNullInt( + ctx, "SELECT MIN(id) FROM certificates WHERE issued >= :issued AND expires >= :now", args, ) @@ -170,6 +171,7 @@ func (c *certChecker) getCerts(unexpiredOnly bool) error { for { certs, err := sa.SelectCertificates( + ctx, c.dbMap, "WHERE id > :id AND issued >= :issued AND expires >= :now ORDER BY id LIMIT :limit", args, @@ -199,9 +201,9 @@ func (c *certChecker) getCerts(unexpiredOnly bool) error { return nil } -func (c *certChecker) processCerts(wg *sync.WaitGroup, badResultsOnly bool, ignoredLints map[string]bool) { +func (c *certChecker) processCerts(ctx context.Context, wg *sync.WaitGroup, badResultsOnly bool, ignoredLints map[string]bool) { for cert := range c.certs { - dnsNames, problems := c.checkCert(cert, ignoredLints) + dnsNames, problems := c.checkCert(ctx, cert, ignoredLints) valid := len(problems) == 0 c.rMu.Lock() if !badResultsOnly || (badResultsOnly && !valid) { @@ -245,8 +247,8 @@ var expectedExtensionContent = map[string][]byte{ // likely valid at the time the certificate was issued. Authorizations with // status = "deactivated" are counted for this, so long as their validatedAt // is before the issuance and expiration is after. -func (c *certChecker) checkValidations(cert core.Certificate, dnsNames []string) error { - authzs, err := sa.SelectAuthzsMatchingIssuance(c.dbMap, cert.RegistrationID, cert.Issued, dnsNames) +func (c *certChecker) checkValidations(ctx context.Context, cert core.Certificate, dnsNames []string) error { + authzs, err := sa.SelectAuthzsMatchingIssuance(ctx, c.dbMap, cert.RegistrationID, cert.Issued, dnsNames) if err != nil { return fmt.Errorf("error checking authzs for certificate %s: %w", cert.Serial, err) } @@ -277,7 +279,7 @@ func (c *certChecker) checkValidations(cert core.Certificate, dnsNames []string) } // checkCert returns a list of DNS names in the certificate and a list of problems with the certificate. -func (c *certChecker) checkCert(cert core.Certificate, ignoredLints map[string]bool) ([]string, []string) { +func (c *certChecker) checkCert(ctx context.Context, cert core.Certificate, ignoredLints map[string]bool) ([]string, []string) { var dnsNames []string var problems []string @@ -386,13 +388,13 @@ func (c *certChecker) checkCert(cert core.Certificate, ignoredLints map[string]b if err != nil { problems = append(problems, fmt.Sprintf("Couldn't parse stored certificate: %s", err)) } - err = c.kp.GoodKey(context.Background(), p.PublicKey) + err = c.kp.GoodKey(ctx, p.PublicKey) if err != nil { problems = append(problems, fmt.Sprintf("Key Policy isn't willing to issue for public key: %s", err)) } if features.Enabled(features.CertCheckerChecksValidations) { - err = c.checkValidations(cert, parsedCert.DNSNames) + err = c.checkValidations(ctx, cert, parsedCert.DNSNames) if err != nil { if features.Enabled(features.CertCheckerRequiresValidations) { problems = append(problems, err.Error()) @@ -531,7 +533,7 @@ func main() { // is finished it will close the certificate channel which allows the range // loops in checker.processCerts to break go func() { - err := checker.getCerts(config.CertChecker.UnexpiredOnly) + err := checker.getCerts(context.TODO(), config.CertChecker.UnexpiredOnly) cmd.FailOnError(err, "Batch retrieval of certificates failed") }() @@ -541,7 +543,7 @@ func main() { wg.Add(1) go func() { s := checker.clock.Now() - checker.processCerts(wg, config.CertChecker.BadResultsOnly, ignoredLintsMap) + checker.processCerts(context.TODO(), wg, config.CertChecker.BadResultsOnly, ignoredLintsMap) checkerLatency.Observe(checker.clock.Since(s).Seconds()) }() } diff --git a/cmd/cert-checker/main_test.go b/cmd/cert-checker/main_test.go index 799e89f74..3b7d24457 100644 --- a/cmd/cert-checker/main_test.go +++ b/cmd/cert-checker/main_test.go @@ -86,7 +86,7 @@ func BenchmarkCheckCert(b *testing.B) { } b.ResetTimer() for i := 0; i < b.N; i++ { - checker.checkCert(cert, nil) + checker.checkCert(context.Background(), cert, nil) } } @@ -130,7 +130,7 @@ func TestCheckWildcardCert(t *testing.T) { Issued: parsed.NotBefore, DER: wildcardCertDer, } - _, problems := checker.checkCert(cert, nil) + _, problems := checker.checkCert(context.Background(), cert, nil) for _, p := range problems { t.Errorf(p) } @@ -163,7 +163,7 @@ func TestCheckCertReturnsDNSNames(t *testing.T) { DER: block.Bytes, } - names, problems := checker.checkCert(cert, nil) + names, problems := checker.checkCert(context.Background(), cert, nil) if !reflect.DeepEqual(names, []string{"quite_invalid.com", "al--so--wr--ong.com"}) { t.Errorf("didn't get expected DNS names. other problems: %s", strings.Join(problems, "\n")) } @@ -268,7 +268,7 @@ func TestCheckCert(t *testing.T) { Expires: goodExpiry.AddDate(0, 0, 2), // Expiration doesn't match } - _, problems := checker.checkCert(cert, nil) + _, problems := checker.checkCert(context.Background(), cert, nil) problemsMap := map[string]int{ "Stored digest doesn't match certificate digest": 1, @@ -294,7 +294,7 @@ func TestCheckCert(t *testing.T) { // Same settings as above, but the stored serial number in the DB is invalid. cert.Serial = "not valid" - _, problems = checker.checkCert(cert, nil) + _, problems = checker.checkCert(context.Background(), cert, nil) foundInvalidSerialProblem := false for _, p := range problems { if p == "Stored serial is invalid" { @@ -319,7 +319,7 @@ func TestCheckCert(t *testing.T) { cert.DER = goodCertDer cert.Expires = parsed.NotAfter cert.Issued = parsed.NotBefore - _, problems = checker.checkCert(cert, nil) + _, problems = checker.checkCert(context.Background(), cert, nil) test.AssertEquals(t, len(problems), 0) }) } @@ -365,12 +365,12 @@ func TestGetAndProcessCerts(t *testing.T) { } batchSize = 2 - err = checker.getCerts(false) + err = checker.getCerts(context.Background(), false) test.AssertNotError(t, err, "Failed to retrieve certificates") test.AssertEquals(t, len(checker.certs), 5) wg := new(sync.WaitGroup) wg.Add(1) - checker.processCerts(wg, false, nil) + checker.processCerts(context.Background(), wg, false, nil) test.AssertEquals(t, checker.issuedReport.BadCerts, int64(5)) test.AssertEquals(t, len(checker.issuedReport.Entries), 5) } @@ -383,7 +383,7 @@ type mismatchedCountDB struct{} // `getCerts` calls `SelectInt` first to determine how many rows there are // matching the `getCertsCountQuery` criteria. For this mock we return // a non-zero number -func (db mismatchedCountDB) SelectNullInt(_ string, _ ...interface{}) (sql.NullInt64, error) { +func (db mismatchedCountDB) SelectNullInt(_ context.Context, _ string, _ ...interface{}) (sql.NullInt64, error) { return sql.NullInt64{ Int64: 99999, Valid: true, @@ -393,7 +393,7 @@ func (db mismatchedCountDB) SelectNullInt(_ string, _ ...interface{}) (sql.NullI // `getCerts` then calls `Select` to retrieve the Certificate rows. We pull // a dastardly switch-a-roo here and return an empty set -func (db mismatchedCountDB) Select(output interface{}, _ string, _ ...interface{}) ([]interface{}, error) { +func (db mismatchedCountDB) Select(_ context.Context, output interface{}, _ string, _ ...interface{}) ([]interface{}, error) { // But actually return nothing outputPtr, _ := output.(*[]sa.CertWithID) *outputPtr = []sa.CertWithID{} @@ -425,7 +425,7 @@ func TestGetCertsEmptyResults(t *testing.T) { checker.dbMap = mismatchedCountDB{} batchSize = 3 - err = checker.getCerts(false) + err = checker.getCerts(context.Background(), false) test.AssertNotError(t, err, "Failed to retrieve certificates") } @@ -437,7 +437,7 @@ type emptyDB struct { // SelectNullInt is a method that returns a false sql.NullInt64 struct to // mock a null DB response -func (db emptyDB) SelectNullInt(_ string, _ ...interface{}) (sql.NullInt64, error) { +func (db emptyDB) SelectNullInt(_ context.Context, _ string, _ ...interface{}) (sql.NullInt64, error) { return sql.NullInt64{Valid: false}, nil } @@ -452,7 +452,7 @@ func TestGetCertsNullResults(t *testing.T) { checker := newChecker(saDbMap, clock.NewFake(), pa, kp, time.Hour, testValidityDurations, blog.NewMock()) checker.dbMap = emptyDB{} - err = checker.getCerts(false) + err = checker.getCerts(context.Background(), false) test.AssertError(t, err, "Should have gotten error from empty DB") } @@ -592,13 +592,13 @@ func TestIgnoredLint(t *testing.T) { // Check the certificate with a nil ignore map. This should return the // expected zlint problems. - _, problems := checker.checkCert(cert, nil) + _, problems := checker.checkCert(context.Background(), cert, nil) sort.Strings(problems) test.AssertDeepEquals(t, problems, expectedProblems) // Check the certificate again with an ignore map that excludes the affected // lints. This should return no problems. - _, problems = checker.checkCert(cert, map[string]bool{ + _, problems = checker.checkCert(context.Background(), cert, map[string]bool{ "e_sub_cert_aia_does_not_contain_ocsp_url": true, "n_subject_common_name_included": true, "w_ct_sct_policy_count_unsatisfied": true, diff --git a/cmd/contact-auditor/main.go b/cmd/contact-auditor/main.go index 7aa4ee470..a20560b6f 100644 --- a/cmd/contact-auditor/main.go +++ b/cmd/contact-auditor/main.go @@ -1,6 +1,7 @@ package notmain import ( + "context" "database/sql" "encoding/json" "errors" @@ -68,8 +69,8 @@ func validateContacts(id int64, createdAt string, contacts []string) error { // beginAuditQuery executes the audit query and returns a cursor used to // stream the results. -func (c contactAuditor) beginAuditQuery() (*sql.Rows, error) { - rows, err := c.db.Query(` +func (c contactAuditor) beginAuditQuery(ctx context.Context) (*sql.Rows, error) { + rows, err := c.db.QueryContext(ctx, ` SELECT DISTINCT id, contact, createdAt FROM registrations WHERE contact NOT IN ('[]', 'null');`) @@ -98,9 +99,9 @@ func (c contactAuditor) writeResults(result string) { // run retrieves a cursor from `beginAuditQuery` and then audits the // `contact` column of all returned rows for abnormalities or policy // violations. -func (c contactAuditor) run(resChan chan *result) error { +func (c contactAuditor) run(ctx context.Context, resChan chan *result) error { c.logger.Infof("Beginning database query") - rows, err := c.beginAuditQuery() + rows, err := c.beginAuditQuery(ctx) if err != nil { return err } @@ -193,7 +194,7 @@ func main() { logger.Info("Running contact-auditor") - err = auditor.run(nil) + err = auditor.run(context.TODO(), nil) cmd.FailOnError(err, "Audit was interrupted, results may be incomplete") logger.Info("Audit finished successfully") diff --git a/cmd/contact-auditor/main_test.go b/cmd/contact-auditor/main_test.go index f49a18970..c9c2a2edf 100644 --- a/cmd/contact-auditor/main_test.go +++ b/cmd/contact-auditor/main_test.go @@ -41,7 +41,7 @@ func TestContactAuditor(t *testing.T) { testCtx.addRegistrations(t) resChan := make(chan *result, 10) - err := testCtx.c.run(resChan) + err := testCtx.c.run(context.Background(), resChan) test.AssertNotError(t, err, "received error") // We should get back A, B, C, and D diff --git a/cmd/expiration-mailer/main.go b/cmd/expiration-mailer/main.go index d41bc5a0b..0e45f5cda 100644 --- a/cmd/expiration-mailer/main.go +++ b/cmd/expiration-mailer/main.go @@ -296,7 +296,7 @@ func (m *mailer) updateLastNagTimestampsChunk(ctx context.Context, certs []*x509 ) params[0] = m.clk.Now() - _, err := m.dbMap.WithContext(ctx).Exec(query, params...) + _, err := m.dbMap.ExecContext(ctx, query, params...) if err != nil { m.log.AuditErrf("Error updating certificate status for %d certs: %s", len(certs), err) m.stats.errorCount.With(prometheus.Labels{"type": "UpdateCertificateStatus"}).Inc() @@ -307,7 +307,8 @@ func (m *mailer) certIsRenewed(ctx context.Context, names []string, issued time. namehash := sa.HashNames(names) var present bool - err := m.dbMap.WithContext(ctx).SelectOne( + err := m.dbMap.SelectOne( + ctx, &present, `SELECT EXISTS (SELECT id FROM fqdnSets WHERE setHash = ? AND issued > ? LIMIT 1)`, namehash, @@ -530,7 +531,8 @@ func (m *mailer) getCertsWithJoin(ctx context.Context, left, right time.Time, ex // sequentially fetch the certificate details. This avoids an expensive // JOIN. var certs []certDERWithRegID - _, err := m.dbMap.WithContext(ctx).Select( + _, err := m.dbMap.Select( + ctx, &certs, `SELECT cert.der as der, cert.registrationID as regID @@ -564,7 +566,8 @@ func (m *mailer) getCerts(ctx context.Context, left, right time.Time, expiresIn // sequentially fetch the certificate details. This avoids an expensive // JOIN. var serials []string - _, err := m.dbMap.WithContext(ctx).Select( + _, err := m.dbMap.Select( + ctx, &serials, `SELECT cs.serial @@ -596,7 +599,7 @@ func (m *mailer) getCerts(ctx context.Context, left, right time.Time, expiresIn return nil, ctx.Err() } var cert core.Certificate - cert, err := sa.SelectCertificate(m.dbMap.WithContext(ctx), serial) + cert, err := sa.SelectCertificate(ctx, m.dbMap, serial) if err != nil { // We can get a NoRowsErr when processing a serial number corresponding // to a precertificate with no final certificate. Since this certificate diff --git a/cmd/expiration-mailer/main_test.go b/cmd/expiration-mailer/main_test.go index 6ef21d349..3c0ad588c 100644 --- a/cmd/expiration-mailer/main_test.go +++ b/cmd/expiration-mailer/main_test.go @@ -329,6 +329,8 @@ func TestNoContactCertIsNotRenewed(t *testing.T) { // An account with no contact info has a certificate that is expiring but has been renewed. // We should only examine that certificate once. func TestNoContactCertIsRenewed(t *testing.T) { + ctx := context.Background() + testCtx := setup(t, []time.Duration{time.Hour * 24 * 7}) reg, err := makeRegistration(testCtx.ssa, 1, jsonKeyA, []string{}) @@ -350,7 +352,7 @@ func TestNoContactCertIsRenewed(t *testing.T) { setupDBMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms) test.AssertNotError(t, err, "setting up DB") - err = setupDBMap.Insert(&core.FQDNSet{ + err = setupDBMap.Insert(ctx, &core.FQDNSet{ SetHash: sa.HashNames(names), Serial: core.SerialToString(serial2), Issued: testCtx.fc.Now().Add(time.Hour), @@ -358,7 +360,7 @@ func TestNoContactCertIsRenewed(t *testing.T) { }) test.AssertNotError(t, err, "inserting FQDNSet for renewal") - err = testCtx.m.findExpiringCertificates(context.Background()) + err = testCtx.m.findExpiringCertificates(ctx) test.AssertNotError(t, err, "finding expired certificates") // We should have examined exactly one certificate @@ -370,7 +372,7 @@ func TestNoContactCertIsRenewed(t *testing.T) { // Run findExpiringCertificates again. The count of examined certificates // should not increase again. - err = testCtx.m.findExpiringCertificates(context.Background()) + err = testCtx.m.findExpiringCertificates(ctx) test.AssertNotError(t, err, "finding expired certificates") test.AssertMetricWithLabelsEquals(t, certsExamined, prometheus.Labels{}, 1.0) test.AssertMetricWithLabelsEquals(t, certsAlreadyRenewed, prometheus.Labels{}, 1.0) @@ -498,6 +500,8 @@ func makeCertificate(regID int64, serial *big.Int, dnsNames []string, expires ti } func insertCertificate(cert certDERWithRegID, lastNagSent time.Time) error { + ctx := context.Background() + parsedCert, err := x509.ParseCertificate(cert.DER) if err != nil { return err @@ -507,7 +511,7 @@ func insertCertificate(cert certDERWithRegID, lastNagSent time.Time) error { if err != nil { return err } - err = setupDBMap.Insert(&core.Certificate{ + err = setupDBMap.Insert(ctx, &core.Certificate{ RegistrationID: cert.RegID, Serial: core.SerialToString(parsedCert.SerialNumber), Issued: parsedCert.NotBefore, @@ -518,7 +522,7 @@ func insertCertificate(cert certDERWithRegID, lastNagSent time.Time) error { return fmt.Errorf("inserting certificate: %w", err) } - return setupDBMap.Insert(&core.CertificateStatus{ + return setupDBMap.Insert(ctx, &core.CertificateStatus{ Serial: core.SerialToString(parsedCert.SerialNumber), LastExpirationNagSent: lastNagSent, Status: core.OCSPStatusGood, @@ -599,9 +603,9 @@ func addExpiringCerts(t *testing.T, ctx *testCtx) []certDERWithRegID { setupDBMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms) test.AssertNotError(t, err, "setting up DB") - err = setupDBMap.Insert(fqdnStatusD) + err = setupDBMap.Insert(context.Background(), fqdnStatusD) test.AssertNotError(t, err, "Couldn't add fqdnStatusD") - err = setupDBMap.Insert(fqdnStatusDRenewed) + err = setupDBMap.Insert(context.Background(), fqdnStatusDRenewed) test.AssertNotError(t, err, "Couldn't add fqdnStatusDRenewed") return []certDERWithRegID{certA, certB, certC, certD} } @@ -752,7 +756,7 @@ func TestCertIsRenewed(t *testing.T) { err = insertCertificate(certDERWithRegID{DER: certDer, RegID: reg.Id}, time.Time{}) test.AssertNotError(t, err, fmt.Sprintf("Couldn't add cert %s", testData.stringSerial)) - err = setupDBMap.Insert(fqdnStatus) + err = setupDBMap.Insert(context.Background(), fqdnStatus) test.AssertNotError(t, err, fmt.Sprintf("Couldn't add fqdnStatus %s", testData.stringSerial)) } @@ -858,13 +862,15 @@ func TestDontFindRevokedCert(t *testing.T) { err = insertCertificate(certA, time.Time{}) test.AssertNotError(t, err, "inserting certificate") + ctx := context.Background() + setupDBMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms) test.AssertNotError(t, err, "sa.NewDbMap failed") - _, err = setupDBMap.Exec("UPDATE certificateStatus SET status = ? WHERE serial = ?", + _, err = setupDBMap.ExecContext(ctx, "UPDATE certificateStatus SET status = ? WHERE serial = ?", string(core.OCSPStatusRevoked), core.SerialToString(serial1)) test.AssertNotError(t, err, "revoking certificate") - err = testCtx.m.findExpiringCertificates(context.Background()) + err = testCtx.m.findExpiringCertificates(ctx) test.AssertNotError(t, err, "err from findExpiringCertificates") if len(testCtx.mc.Messages) != 0 { diff --git a/cmd/id-exporter/main.go b/cmd/id-exporter/main.go index 34bbd25f0..d4d526a26 100644 --- a/cmd/id-exporter/main.go +++ b/cmd/id-exporter/main.go @@ -2,6 +2,7 @@ package notmain import ( "bufio" + "context" "encoding/json" "errors" "flag" @@ -68,9 +69,10 @@ func (i *idExporterResults) writeToFile(outfile string) error { } // findIDs gathers all registration IDs with unexpired certificates. -func (c idExporter) findIDs() (idExporterResults, error) { +func (c idExporter) findIDs(ctx context.Context) (idExporterResults, error) { var holder idExporterResults _, err := c.dbMap.Select( + ctx, &holder, `SELECT DISTINCT r.id FROM registrations AS r @@ -89,9 +91,10 @@ func (c idExporter) findIDs() (idExporterResults, error) { // findIDsWithExampleHostnames gathers all registration IDs with // unexpired certificates and a corresponding example hostname. -func (c idExporter) findIDsWithExampleHostnames() (idExporterResults, error) { +func (c idExporter) findIDsWithExampleHostnames(ctx context.Context) (idExporterResults, error) { var holder idExporterResults _, err := c.dbMap.Select( + ctx, &holder, `SELECT SQL_BIG_RESULT cert.registrationID AS id, @@ -116,13 +119,14 @@ func (c idExporter) findIDsWithExampleHostnames() (idExporterResults, error) { // findIDsForHostnames gathers all registration IDs with unexpired // certificates for each `hostnames` entry. -func (c idExporter) findIDsForHostnames(hostnames []string) (idExporterResults, error) { +func (c idExporter) findIDsForHostnames(ctx context.Context, hostnames []string) (idExporterResults, error) { var holder idExporterResults for _, hostname := range hostnames { - // Pass the same list in each time, gorp will happily just append to the slice + // Pass the same list in each time, borp will happily just append to the slice // instead of overwriting it each time - // https://github.com/go-gorp/gorp/blob/2ae7d174a4cf270240c4561092402affba25da5e/select.go#L348-L355 + // https://github.com/letsencrypt/borp/blob/c87bd6443d59746a33aca77db34a60cfc344adb2/select.go#L349-L353 _, err := c.dbMap.Select( + ctx, &holder, `SELECT DISTINCT c.registrationID AS id FROM certificates AS c @@ -279,15 +283,15 @@ func main() { hostnames, err := unmarshalHostnames(*hostnamesFile) cmd.FailOnError(err, "Problem unmarshalling hostnames") - results, err = exporter.findIDsForHostnames(hostnames) + results, err = exporter.findIDsForHostnames(context.TODO(), hostnames) cmd.FailOnError(err, "Could not find IDs for hostnames") } else if *withExampleHostnames { - results, err = exporter.findIDsWithExampleHostnames() + results, err = exporter.findIDsWithExampleHostnames(context.TODO()) cmd.FailOnError(err, "Could not find IDs with hostnames") } else { - results, err = exporter.findIDs() + results, err = exporter.findIDs(context.TODO()) cmd.FailOnError(err, "Could not find IDs") } diff --git a/cmd/id-exporter/main_test.go b/cmd/id-exporter/main_test.go index 930699e41..20fdec760 100644 --- a/cmd/id-exporter/main_test.go +++ b/cmd/id-exporter/main_test.go @@ -41,6 +41,8 @@ const ( ) func TestFindIDs(t *testing.T) { + ctx := context.Background() + testCtx := setup(t) defer testCtx.cleanUp() @@ -49,7 +51,7 @@ func TestFindIDs(t *testing.T) { // Run findIDs - since no certificates have been added corresponding to // the above registrations, no IDs should be found. - results, err := testCtx.c.findIDs() + results, err := testCtx.c.findIDs(ctx) test.AssertNotError(t, err, "findIDs() produced error") test.AssertEquals(t, len(results), 0) @@ -61,7 +63,7 @@ func TestFindIDs(t *testing.T) { // *not* be present since their certificate has already expired. Unlike // previous versions of this test RegD is not filtered out for having a `tel:` // contact field anymore - this is the duty of the notify-mailer. - results, err = testCtx.c.findIDs() + results, err = testCtx.c.findIDs(ctx) test.AssertNotError(t, err, "findIDs() produced error") test.AssertEquals(t, len(results), 3) for _, entry := range results { @@ -76,7 +78,7 @@ func TestFindIDs(t *testing.T) { // Allow a 1 year grace period testCtx.c.grace = 360 * 24 * time.Hour - results, err = testCtx.c.findIDs() + results, err = testCtx.c.findIDs(ctx) test.AssertNotError(t, err, "findIDs() produced error") // Now all four registration should be returned, including RegB since its // certificate expired within the grace period @@ -93,6 +95,7 @@ func TestFindIDs(t *testing.T) { } func TestFindIDsWithExampleHostnames(t *testing.T) { + ctx := context.Background() testCtx := setup(t) defer testCtx.cleanUp() @@ -102,7 +105,7 @@ func TestFindIDsWithExampleHostnames(t *testing.T) { // Run findIDsWithExampleHostnames - since no certificates have been // added corresponding to the above registrations, no IDs should be // found. - results, err := testCtx.c.findIDsWithExampleHostnames() + results, err := testCtx.c.findIDsWithExampleHostnames(ctx) test.AssertNotError(t, err, "findIDs() produced error") test.AssertEquals(t, len(results), 0) @@ -113,7 +116,7 @@ func TestFindIDsWithExampleHostnames(t *testing.T) { // registrations with unexpired certs we should get exactly three // IDs back: RegA, RegC and RegD. RegB should *not* be present since // their certificate has already expired. - results, err = testCtx.c.findIDsWithExampleHostnames() + results, err = testCtx.c.findIDsWithExampleHostnames(ctx) test.AssertNotError(t, err, "findIDs() produced error") test.AssertEquals(t, len(results), 3) for _, entry := range results { @@ -131,7 +134,7 @@ func TestFindIDsWithExampleHostnames(t *testing.T) { // Allow a 1 year grace period testCtx.c.grace = 360 * 24 * time.Hour - results, err = testCtx.c.findIDsWithExampleHostnames() + results, err = testCtx.c.findIDsWithExampleHostnames(ctx) test.AssertNotError(t, err, "findIDs() produced error") // Now all four registrations should be returned, including RegB @@ -154,6 +157,8 @@ func TestFindIDsWithExampleHostnames(t *testing.T) { } func TestFindIDsForHostnames(t *testing.T) { + ctx := context.Background() + testCtx := setup(t) defer testCtx.cleanUp() @@ -162,14 +167,14 @@ func TestFindIDsForHostnames(t *testing.T) { // Run findIDsForHostnames - since no certificates have been added corresponding to // the above registrations, no IDs should be found. - results, err := testCtx.c.findIDsForHostnames([]string{"example-a.com", "example-b.com", "example-c.com", "example-d.com"}) + results, err := testCtx.c.findIDsForHostnames(ctx, []string{"example-a.com", "example-b.com", "example-c.com", "example-d.com"}) test.AssertNotError(t, err, "findIDs() produced error") test.AssertEquals(t, len(results), 0) // Now add some certificates testCtx.addCertificates(t) - results, err = testCtx.c.findIDsForHostnames([]string{"example-a.com", "example-b.com", "example-c.com", "example-d.com"}) + results, err = testCtx.c.findIDsForHostnames(ctx, []string{"example-a.com", "example-b.com", "example-c.com", "example-d.com"}) test.AssertNotError(t, err, "findIDsForHostnames() failed") test.AssertEquals(t, len(results), 3) for _, entry := range results { @@ -314,6 +319,7 @@ func (tc testCtx) addRegistrations(t *testing.T) { } func (tc testCtx) addCertificates(t *testing.T) { + ctx := context.Background() serial1 := big.NewInt(1336) serial1String := core.SerialToString(serial1) serial2 := big.NewInt(1337) @@ -352,9 +358,10 @@ func (tc testCtx) addCertificates(t *testing.T) { Expires: rawCertA.NotAfter, DER: certDerA, } - err := tc.c.dbMap.Insert(certA) + err := tc.c.dbMap.Insert(ctx, certA) test.AssertNotError(t, err, "Couldn't add certA") - _, err = tc.c.dbMap.Exec( + _, err = tc.c.dbMap.ExecContext( + ctx, "INSERT INTO issuedNames (reversedName, serial, notBefore) VALUES (?,?,0)", "com.example-a", serial1String, @@ -377,9 +384,10 @@ func (tc testCtx) addCertificates(t *testing.T) { Expires: rawCertB.NotAfter, DER: certDerB, } - err = tc.c.dbMap.Insert(certB) + err = tc.c.dbMap.Insert(ctx, certB) test.AssertNotError(t, err, "Couldn't add certB") - _, err = tc.c.dbMap.Exec( + _, err = tc.c.dbMap.ExecContext( + ctx, "INSERT INTO issuedNames (reversedName, serial, notBefore) VALUES (?,?,0)", "com.example-b", serial2String, @@ -402,9 +410,10 @@ func (tc testCtx) addCertificates(t *testing.T) { Expires: rawCertC.NotAfter, DER: certDerC, } - err = tc.c.dbMap.Insert(certC) + err = tc.c.dbMap.Insert(ctx, certC) test.AssertNotError(t, err, "Couldn't add certC") - _, err = tc.c.dbMap.Exec( + _, err = tc.c.dbMap.ExecContext( + ctx, "INSERT INTO issuedNames (reversedName, serial, notBefore) VALUES (?,?,0)", "com.example-c", serial3String, @@ -427,9 +436,10 @@ func (tc testCtx) addCertificates(t *testing.T) { Expires: rawCertD.NotAfter, DER: certDerD, } - err = tc.c.dbMap.Insert(certD) + err = tc.c.dbMap.Insert(ctx, certD) test.AssertNotError(t, err, "Couldn't add certD") - _, err = tc.c.dbMap.Exec( + _, err = tc.c.dbMap.ExecContext( + ctx, "INSERT INTO issuedNames (reversedName, serial, notBefore) VALUES (?,?,0)", "com.example-d", serial4String, diff --git a/cmd/notify-mailer/main.go b/cmd/notify-mailer/main.go index e4b9ef739..8794a4b42 100644 --- a/cmd/notify-mailer/main.go +++ b/cmd/notify-mailer/main.go @@ -1,6 +1,7 @@ package notmain import ( + "context" "encoding/csv" "encoding/json" "errors" @@ -119,7 +120,7 @@ func (m *mailer) makeMessageBody(recipients []recipient) (string, error) { return messageBody.String(), nil } -func (m *mailer) run() error { +func (m *mailer) run(ctx context.Context) error { err := m.ok() if err != nil { return err @@ -128,7 +129,7 @@ func (m *mailer) run() error { totalRecipients := len(m.recipients) m.log.Infof("Resolving addresses for (%d) recipients", totalRecipients) - addressToRecipient, err := m.resolveAddresses() + addressToRecipient, err := m.resolveAddresses(ctx) if err != nil { return err } @@ -238,10 +239,10 @@ func (m *mailer) run() error { // resolveAddresses creates a mapping of email addresses to (a list of) // `recipient`s that resolve to that email address. -func (m *mailer) resolveAddresses() (addressToRecipientMap, error) { +func (m *mailer) resolveAddresses(ctx context.Context) (addressToRecipientMap, error) { result := make(addressToRecipientMap, len(m.recipients)) for _, recipient := range m.recipients { - addresses, err := getAddressForID(recipient.id, m.dbMap) + addresses, err := getAddressForID(ctx, recipient.id, m.dbMap) if err != nil { return nil, err } @@ -258,17 +259,17 @@ func (m *mailer) resolveAddresses() (addressToRecipientMap, error) { return result, nil } -// dbSelector abstracts over a subset of methods from `gorp.DbMap` objects to +// dbSelector abstracts over a subset of methods from `borp.DbMap` objects to // facilitate mocking in unit tests. type dbSelector interface { - SelectOne(holder interface{}, query string, args ...interface{}) error + SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error } // getAddressForID queries the database for the email address associated with // the provided registration ID. -func getAddressForID(id int64, dbMap dbSelector) ([]string, error) { +func getAddressForID(ctx context.Context, id int64, dbMap dbSelector) ([]string, error) { var result contactQueryResult - err := dbMap.SelectOne(&result, + err := dbMap.SelectOne(ctx, &result, `SELECT id, contact FROM registrations @@ -606,7 +607,7 @@ func main() { parallelSends: *parallelSends, } - err = m.run() + err = m.run(context.TODO()) cmd.FailOnError(err, "Couldn't complete") log.Info("Completed successfully") diff --git a/cmd/notify-mailer/main_test.go b/cmd/notify-mailer/main_test.go index 53b9dd53f..4f57069f8 100644 --- a/cmd/notify-mailer/main_test.go +++ b/cmd/notify-mailer/main_test.go @@ -1,6 +1,7 @@ package notmain import ( + "context" "database/sql" "errors" "fmt" @@ -294,7 +295,7 @@ func TestSleepInterval(t *testing.T) { // Call run() - this should sleep `sleepLen` per destination address // After it returns, we expect (sleepLen * number of destinations) seconds has // elapsed - err := m.run() + err := m.run(context.Background()) test.AssertNotError(t, err, "error calling mailer run()") expectedEnd := clock.NewFake() expectedEnd.Add(time.Second * time.Duration(sleepLen*len(recipients))) @@ -314,7 +315,7 @@ func TestSleepInterval(t *testing.T) { // Call run() - this should blast through all destinations without sleep // After it returns, we expect no clock time to have elapsed on the fake clock - err = m.run() + err = m.run(context.Background()) test.AssertNotError(t, err, "error calling mailer run()") expectedEnd = clock.NewFake() test.AssertEquals(t, m.clk.Now(), expectedEnd.Now()) @@ -345,7 +346,7 @@ func TestMailIntervals(t *testing.T) { // Run the mailer. It should produce an error about the interval start mc.Clear() - err := m.run() + err := m.run(context.Background()) test.AssertError(t, err, "expected error") test.AssertEquals(t, len(mc.Messages), 0) @@ -364,7 +365,7 @@ func TestMailIntervals(t *testing.T) { // Run the mailer. It should produce an error about the sleep interval mc.Clear() - err = m.run() + err = m.run(context.Background()) test.AssertEquals(t, len(mc.Messages), 0) test.AssertEquals(t, err.Error(), "sleep interval (-10) is < 0") @@ -386,7 +387,7 @@ func TestMailIntervals(t *testing.T) { // test-example-updated@letsencrypt.org (beginning of the range), // and one to test-test-test@letsencrypt.org. mc.Clear() - err = m.run() + err = m.run(context.Background()) test.AssertNotError(t, err, "run() produced an error") test.AssertEquals(t, len(mc.Messages), 2) test.AssertEquals(t, mocks.MailerMessage{ @@ -417,7 +418,7 @@ func TestMailIntervals(t *testing.T) { // Run the mailer. Two messages should have been produced, one to // example@letsencrypt.org (ID 1), one to example-example-example@example.com (ID 2) mc.Clear() - err = m.run() + err = m.run(context.Background()) test.AssertNotError(t, err, "run() produced an error") test.AssertEquals(t, len(mc.Messages), 2) test.AssertEquals(t, mocks.MailerMessage{ @@ -456,7 +457,7 @@ func TestParallelism(t *testing.T) { } mc.Clear() - err := m.run() + err := m.run(context.Background()) test.AssertNotError(t, err, "run() produced an error") // The fake clock should have advanced 9 seconds, one for each parallel @@ -499,7 +500,7 @@ func TestMessageContentStatic(t *testing.T) { // Run the mailer, one message should have been created with the content // expected - err := m.run() + err := m.run(context.Background()) test.AssertNotError(t, err, "error calling mailer run()") test.AssertEquals(t, len(mc.Messages), 1) test.AssertEquals(t, mocks.MailerMessage{ @@ -536,7 +537,7 @@ func TestMessageContentInterpolated(t *testing.T) { // Run the mailer, one message should have been created with the content // expected - err := m.run() + err := m.run(context.Background()) test.AssertNotError(t, err, "error calling mailer run()") test.AssertEquals(t, len(mc.Messages), 1) test.AssertEquals(t, mocks.MailerMessage{ @@ -594,7 +595,7 @@ func TestMessageContentInterpolatedMultiple(t *testing.T) { // Run the mailer, one message should have been created with the content // expected - err := m.run() + err := m.run(context.Background()) test.AssertNotError(t, err, "error calling mailer run()") test.AssertEquals(t, len(mc.Messages), 1) test.AssertEquals(t, mocks.MailerMessage{ @@ -616,7 +617,7 @@ type mockEmailResolver struct{} // the `mockEmailResolver` select method treats the requested reg ID as an index // into a list of anonymous structs -func (bs mockEmailResolver) SelectOne(output interface{}, _ string, args ...interface{}) error { +func (bs mockEmailResolver) SelectOne(ctx context.Context, output interface{}, _ string, args ...interface{}) error { // The "dbList" is just a list of contact records in memory dbList := []contactQueryResult{ { @@ -762,7 +763,7 @@ func TestResolveEmails(t *testing.T) { clk: clock.NewFake(), } - addressesToRecipients, err := m.resolveAddresses() + addressesToRecipients, err := m.resolveAddresses(context.Background()) test.AssertNotError(t, err, "failed to resolveEmailAddresses") expected := []string{ diff --git a/cmd/rocsp-tool/client.go b/cmd/rocsp-tool/client.go index 37c70c0f4..1b89c19c1 100644 --- a/cmd/rocsp-tool/client.go +++ b/cmd/rocsp-tool/client.go @@ -44,7 +44,8 @@ func getStartingID(ctx context.Context, clk clock.Clock, db *db.WrappedMap) (int // certificates. startTime := clk.Now().Add(-24 * time.Hour) var minID *int64 - err := db.WithContext(ctx).QueryRow( + err := db.QueryRowContext( + ctx, "SELECT MIN(id) FROM certificateStatus WHERE notAfter >= ?", startTime, ).Scan(&minID) @@ -70,7 +71,8 @@ func (cl *client) loadFromDB(ctx context.Context, speed ProcessingSpeed, startFr // Find the current maximum id in certificateStatus. We do this because the table is always // growing. If we scanned until we saw a batch with no rows, we would scan forever. var maxID *int64 - err = cl.db.WithContext(ctx).QueryRow( + err = cl.db.QueryRowContext( + ctx, "SELECT MAX(id) FROM certificateStatus", ).Scan(&maxID) if err != nil { @@ -162,7 +164,7 @@ func (cl *client) scanFromDBOneBatch(ctx context.Context, prevID int64, frequenc return -1, fmt.Errorf("initializing db map: %w", err) } - rows, err := selector.Query(ctx, clauses, params...) + rows, err := selector.QueryContext(ctx, clauses, params...) if err != nil { return -1, fmt.Errorf("scanning certificateStatus: %w", err) } diff --git a/cmd/rocsp-tool/client_test.go b/cmd/rocsp-tool/client_test.go index 658a2c30b..9fffb6c03 100644 --- a/cmd/rocsp-tool/client_test.go +++ b/cmd/rocsp-tool/client_test.go @@ -50,6 +50,8 @@ func makeClient() (*rocsp.RWClient, clock.Clock) { } func TestGetStartingID(t *testing.T) { + ctx := context.Background() + clk := clock.NewFake() dbMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms) test.AssertNotError(t, err, "failed setting up db client") @@ -60,7 +62,7 @@ func TestGetStartingID(t *testing.T) { Serial: "1337", NotAfter: clk.Now().Add(12 * time.Hour), } - err = dbMap.Insert(&cs) + err = dbMap.Insert(ctx, &cs) test.AssertNotError(t, err, "inserting certificate status") firstID := cs.ID @@ -68,7 +70,7 @@ func TestGetStartingID(t *testing.T) { Serial: "1338", NotAfter: clk.Now().Add(36 * time.Hour), } - err = dbMap.Insert(&cs) + err = dbMap.Insert(ctx, &cs) test.AssertNotError(t, err, "inserting certificate status") secondID := cs.ID t.Logf("first ID %d, second ID %d", firstID, secondID) @@ -129,7 +131,7 @@ func TestLoadFromDB(t *testing.T) { defer test.ResetBoulderTestDatabase(t) for i := 0; i < 100; i++ { - err = dbMap.Insert(&core.CertificateStatus{ + err = dbMap.Insert(context.Background(), &core.CertificateStatus{ Serial: fmt.Sprintf("%036x", i), NotAfter: clk.Now().Add(200 * time.Hour), OCSPLastUpdated: clk.Now(), diff --git a/db/gorm.go b/db/gorm.go index ad0c51baf..112eddcff 100644 --- a/db/gorm.go +++ b/db/gorm.go @@ -51,7 +51,7 @@ func NewMappedSelector[T any](executor MappedExecutor) (MappedSelector[T], error // - Note that the reverse is not true: it's perfectly okay for there to be // database columns which do not correspond to fields in the struct; those // columns will be ignored. - // TODO: In the future, when we replace gorp's TableMap with our own, this + // TODO: In the future, when we replace borp's TableMap with our own, this // check should be performed at the time the mapping is declared. columns := make([]string, 0) seen := make(map[string]struct{}) @@ -80,21 +80,21 @@ type mappedSelector[T any] struct { columns []string } -// Query performs a SELECT on the appropriate table for T. It combines the best -// features of gorp, the go stdlib, and generics, using the type parameter of +// QueryContext performs a SELECT on the appropriate table for T. It combines the best +// features of borp, the go stdlib, and generics, using the type parameter of // the typeSelector object to automatically look up the proper table name and // columns to select. It returns an iterable which yields fully-populated // objects of the parameterized type directly. The given clauses MUST be only // the bits of a sql query from "WHERE ..." onwards; if they contain any of the // "SELECT ... FROM ..." portion of the query it will result in an error. The -// args take the same kinds of values as gorp's SELECT: either one argument per +// args take the same kinds of values as borp's SELECT: either one argument per // positional placeholder, or a map of placeholder names to their arguments -// (see https://pkg.go.dev/gopkg.in/gorp.v2#readme-ad-hoc-sql). +// (see https://pkg.go.dev/github.com/letsencrypt/borp#readme-ad-hoc-sql). // // The caller is responsible for calling `Rows.Close()` when they are done with // the query. The caller is also responsible for ensuring that the clauses // argument does not contain any user-influenced input. -func (ts mappedSelector[T]) Query(ctx context.Context, clauses string, args ...interface{}) (Rows[T], error) { +func (ts mappedSelector[T]) QueryContext(ctx context.Context, clauses string, args ...interface{}) (Rows[T], error) { // Look up the table to use based on the type of this TypeSelector. var throwaway T tableMap, err := ts.wrapped.TableFor(reflect.TypeOf(throwaway), false) @@ -106,7 +106,7 @@ func (ts mappedSelector[T]) Query(ctx context.Context, clauses string, args ...i } // QueryFrom is the same as Query, but it additionally takes a table name to -// select from, rather than automatically computing the table name from gorp's +// select from, rather than automatically computing the table name from borp's // DbMap. // // The caller is responsible for calling `Rows.Close()` when they are done with @@ -127,7 +127,7 @@ func (ts mappedSelector[T]) QueryFrom(ctx context.Context, tablename string, cla clauses, ) - r, err := ts.wrapped.WithContext(ctx).Query(query, args...) + r, err := ts.wrapped.QueryContext(ctx, query, args...) if err != nil { return nil, fmt.Errorf("reading db: %w", err) } diff --git a/db/interfaces.go b/db/interfaces.go index d01b42ac2..384adde0c 100644 --- a/db/interfaces.go +++ b/db/interfaces.go @@ -6,7 +6,7 @@ import ( "errors" "reflect" - "github.com/go-gorp/gorp/v3" + "github.com/letsencrypt/borp" ) // These interfaces exist to aid in mocking database operations for unit tests. @@ -18,26 +18,26 @@ import ( // A OneSelector is anything that provides a `SelectOne` function. type OneSelector interface { - SelectOne(interface{}, string, ...interface{}) error + SelectOne(context.Context, interface{}, string, ...interface{}) error } // A Selector is anything that provides a `Select` function. type Selector interface { - Select(interface{}, string, ...interface{}) ([]interface{}, error) + Select(context.Context, interface{}, string, ...interface{}) ([]interface{}, error) } // A Inserter is anything that provides an `Insert` function type Inserter interface { - Insert(list ...interface{}) error + Insert(context.Context, ...interface{}) error } -// A Execer is anything that provides an `Exec` function +// A Execer is anything that provides an `ExecContext` function type Execer interface { - Exec(string, ...interface{}) (sql.Result, error) + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) } -// SelectExecer offers a subset of gorp.SqlExecutor's methods: Select and -// Exec. +// SelectExecer offers a subset of borp.SqlExecutor's methods: Select and +// ExecContext. type SelectExecer interface { Selector Execer @@ -49,48 +49,46 @@ type DatabaseMap interface { OneSelector Inserter SelectExecer - Begin() (Transaction, error) + BeginTx(context.Context) (Transaction, error) } // Executor offers the full combination of OneSelector, Inserter, SelectExecer -// and adds a handful of other high level Gorp methods we use in Boulder. +// and adds a handful of other high level borp methods we use in Boulder. type Executor interface { OneSelector Inserter SelectExecer - Delete(...interface{}) (int64, error) - Get(interface{}, ...interface{}) (interface{}, error) - Update(...interface{}) (int64, error) - Query(string, ...interface{}) (*sql.Rows, error) + Queryer + Delete(context.Context, ...interface{}) (int64, error) + Get(context.Context, interface{}, ...interface{}) (interface{}, error) + Update(context.Context, ...interface{}) (int64, error) } -// Queryer offers the Query method. Note that this is not read-only (i.e. not -// Selector), since a Query can be `INSERT`, `UPDATE`, etc. The difference -// between Query and Exec is that Query can return rows. So for instance it is +// Queryer offers the QueryContext method. Note that this is not read-only (i.e. not +// Selector), since a QueryContext can be `INSERT`, `UPDATE`, etc. The difference +// between QueryContext and ExecContext is that QueryContext can return rows. So for instance it is // suitable for inserting rows and getting back ids. type Queryer interface { - Query(string, ...interface{}) (*sql.Rows, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) } -// Transaction extends an Executor and adds Rollback, Commit, and WithContext. +// Transaction extends an Executor and adds Rollback and Commit type Transaction interface { Executor Rollback() error Commit() error - WithContext(ctx context.Context) gorp.SqlExecutor } -// MappedExecutor is anything that can map types to tables, and which can -// produce a SqlExecutor bound to a context. +// MappedExecutor is anything that can map types to tables type MappedExecutor interface { - TableFor(reflect.Type, bool) (*gorp.TableMap, error) - WithContext(ctx context.Context) gorp.SqlExecutor + TableFor(reflect.Type, bool) (*borp.TableMap, error) + QueryContext(ctx context.Context, clauses string, args ...interface{}) (*sql.Rows, error) } // MappedSelector is anything that can execute various kinds of SQL statements // against a table automatically determined from the parameterized type. type MappedSelector[T any] interface { - Query(ctx context.Context, clauses string, args ...interface{}) (Rows[T], error) + QueryContext(ctx context.Context, clauses string, args ...interface{}) (Rows[T], error) QueryFrom(ctx context.Context, tablename string, clauses string, args ...interface{}) (Rows[T], error) } @@ -105,8 +103,8 @@ type Rows[T any] interface { // MockSqlExecuter implement SqlExecutor by returning errors from every call. // -// To mock out WithContext, we need to be able to return objects that satisfy -// gorp.SqlExecutor. That's a pretty big interface, so we specify one no-op mock +// TODO: To mock out WithContext, we needed to be able to return objects that satisfy +// borp.SqlExecutor. That's a pretty big interface, so we specify one no-op mock // that we can embed everywhere we need to satisfy it. // Note: MockSqlExecutor does *not* implement WithContext. The expectation is // that structs that embed MockSqlExecutor will define their own WithContext @@ -114,48 +112,48 @@ type Rows[T any] interface { // to override the specific methods they need to implement (e.g. SelectOne). type MockSqlExecutor struct{} -func (mse MockSqlExecutor) Get(i interface{}, keys ...interface{}) (interface{}, error) { +func (mse MockSqlExecutor) Get(ctx context.Context, i interface{}, keys ...interface{}) (interface{}, error) { return nil, errors.New("unimplemented") } -func (mse MockSqlExecutor) Insert(list ...interface{}) error { +func (mse MockSqlExecutor) Insert(ctx context.Context, list ...interface{}) error { return errors.New("unimplemented") } -func (mse MockSqlExecutor) Update(list ...interface{}) (int64, error) { +func (mse MockSqlExecutor) Update(ctx context.Context, list ...interface{}) (int64, error) { return 0, errors.New("unimplemented") } -func (mse MockSqlExecutor) Delete(list ...interface{}) (int64, error) { +func (mse MockSqlExecutor) Delete(ctx context.Context, list ...interface{}) (int64, error) { return 0, errors.New("unimplemented") } -func (mse MockSqlExecutor) Exec(query string, args ...interface{}) (sql.Result, error) { +func (mse MockSqlExecutor) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { return nil, errors.New("unimplemented") } -func (mse MockSqlExecutor) Select(i interface{}, query string, args ...interface{}) ([]interface{}, error) { +func (mse MockSqlExecutor) Select(ctx context.Context, i interface{}, query string, args ...interface{}) ([]interface{}, error) { return nil, errors.New("unimplemented") } -func (mse MockSqlExecutor) SelectInt(query string, args ...interface{}) (int64, error) { +func (mse MockSqlExecutor) SelectInt(ctx context.Context, query string, args ...interface{}) (int64, error) { return 0, errors.New("unimplemented") } -func (mse MockSqlExecutor) SelectNullInt(query string, args ...interface{}) (sql.NullInt64, error) { +func (mse MockSqlExecutor) SelectNullInt(ctx context.Context, query string, args ...interface{}) (sql.NullInt64, error) { return sql.NullInt64{}, errors.New("unimplemented") } -func (mse MockSqlExecutor) SelectFloat(query string, args ...interface{}) (float64, error) { +func (mse MockSqlExecutor) SelectFloat(ctx context.Context, query string, args ...interface{}) (float64, error) { return 0, errors.New("unimplemented") } -func (mse MockSqlExecutor) SelectNullFloat(query string, args ...interface{}) (sql.NullFloat64, error) { +func (mse MockSqlExecutor) SelectNullFloat(ctx context.Context, query string, args ...interface{}) (sql.NullFloat64, error) { return sql.NullFloat64{}, errors.New("unimplemented") } -func (mse MockSqlExecutor) SelectStr(query string, args ...interface{}) (string, error) { +func (mse MockSqlExecutor) SelectStr(ctx context.Context, query string, args ...interface{}) (string, error) { return "", errors.New("unimplemented") } -func (mse MockSqlExecutor) SelectNullStr(query string, args ...interface{}) (sql.NullString, error) { +func (mse MockSqlExecutor) SelectNullStr(ctx context.Context, query string, args ...interface{}) (sql.NullString, error) { return sql.NullString{}, errors.New("unimplemented") } -func (mse MockSqlExecutor) SelectOne(holder interface{}, query string, args ...interface{}) error { +func (mse MockSqlExecutor) SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error { return errors.New("unimplemented") } -func (mse MockSqlExecutor) Query(query string, args ...interface{}) (*sql.Rows, error) { +func (mse MockSqlExecutor) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { return nil, errors.New("unimplemented") } -func (mse MockSqlExecutor) QueryRow(query string, args ...interface{}) *sql.Row { +func (mse MockSqlExecutor) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row { return nil } diff --git a/db/map.go b/db/map.go index c79182500..4de5fcabf 100644 --- a/db/map.go +++ b/db/map.go @@ -5,10 +5,11 @@ import ( "database/sql" "errors" "fmt" + "reflect" "regexp" - gorp "github.com/go-gorp/gorp/v3" "github.com/go-sql-driver/mysql" + "github.com/letsencrypt/borp" ) // ErrDatabaseOp wraps an underlying err with a description of the operation @@ -44,7 +45,7 @@ func (e ErrDatabaseOp) Unwrap() error { // IsNoRows is a utility function for determining if an error wraps the go sql // package's ErrNoRows, which is returned when a Scan operation has no more -// results to return, and as such is returned by many gorp methods. +// results to return, and as such is returned by many borp methods. func IsNoRows(err error) bool { return errors.Is(err, sql.ErrNoRows) } @@ -57,50 +58,74 @@ func IsDuplicate(err error) bool { return errors.As(err, &dbErr) && dbErr.Number == 1062 } -// WrappedMap wraps a *gorp.DbMap such that its major functions wrap error +// WrappedMap wraps a *borp.DbMap such that its major functions wrap error // results in ErrDatabaseOp instances before returning them to the caller. type WrappedMap struct { - *gorp.DbMap + dbMap *borp.DbMap } -func (m *WrappedMap) Get(holder interface{}, keys ...interface{}) (interface{}, error) { - return WrappedExecutor{SqlExecutor: m.DbMap}.Get(holder, keys...) +func NewWrappedMap(dbMap *borp.DbMap) *WrappedMap { + return &WrappedMap{dbMap: dbMap} } -func (m *WrappedMap) Insert(list ...interface{}) error { - return WrappedExecutor{SqlExecutor: m.DbMap}.Insert(list...) +func (m *WrappedMap) SQLDb() *sql.DB { + return m.dbMap.Db } -func (m *WrappedMap) Update(list ...interface{}) (int64, error) { - return WrappedExecutor{SqlExecutor: m.DbMap}.Update(list...) +func (m *WrappedMap) BorpDB() *borp.DbMap { + return m.dbMap } -func (m *WrappedMap) Delete(list ...interface{}) (int64, error) { - return WrappedExecutor{SqlExecutor: m.DbMap}.Delete(list...) +func (m *WrappedMap) TableFor(t reflect.Type, checkPK bool) (*borp.TableMap, error) { + return m.dbMap.TableFor(t, checkPK) } -func (m *WrappedMap) Select(holder interface{}, query string, args ...interface{}) ([]interface{}, error) { - return WrappedExecutor{SqlExecutor: m.DbMap}.Select(holder, query, args...) +func (m *WrappedMap) Get(ctx context.Context, holder interface{}, keys ...interface{}) (interface{}, error) { + return WrappedExecutor{sqlExecutor: m.dbMap}.Get(ctx, holder, keys...) } -func (m *WrappedMap) SelectOne(holder interface{}, query string, args ...interface{}) error { - return WrappedExecutor{SqlExecutor: m.DbMap}.SelectOne(holder, query, args...) +func (m *WrappedMap) Insert(ctx context.Context, list ...interface{}) error { + return WrappedExecutor{sqlExecutor: m.dbMap}.Insert(ctx, list...) } -func (m *WrappedMap) Query(query string, args ...interface{}) (*sql.Rows, error) { - return WrappedExecutor{SqlExecutor: m.DbMap}.Query(query, args...) +func (m *WrappedMap) Update(ctx context.Context, list ...interface{}) (int64, error) { + return WrappedExecutor{sqlExecutor: m.dbMap}.Update(ctx, list...) } -func (m *WrappedMap) Exec(query string, args ...interface{}) (sql.Result, error) { - return WrappedExecutor{SqlExecutor: m.DbMap}.Exec(query, args...) +func (m *WrappedMap) Delete(ctx context.Context, list ...interface{}) (int64, error) { + return WrappedExecutor{sqlExecutor: m.dbMap}.Delete(ctx, list...) } -func (m *WrappedMap) WithContext(ctx context.Context) gorp.SqlExecutor { - return WrappedExecutor{SqlExecutor: m.DbMap.WithContext(ctx)} +func (m *WrappedMap) Select(ctx context.Context, holder interface{}, query string, args ...interface{}) ([]interface{}, error) { + return WrappedExecutor{sqlExecutor: m.dbMap}.Select(ctx, holder, query, args...) } -func (m *WrappedMap) Begin() (Transaction, error) { - tx, err := m.DbMap.Begin() +func (m *WrappedMap) SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error { + return WrappedExecutor{sqlExecutor: m.dbMap}.SelectOne(ctx, holder, query, args...) +} + +func (m *WrappedMap) SelectNullInt(ctx context.Context, query string, args ...interface{}) (sql.NullInt64, error) { + return WrappedExecutor{sqlExecutor: m.dbMap}.SelectNullInt(ctx, query, args...) +} + +func (m *WrappedMap) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { + return WrappedExecutor{sqlExecutor: m.dbMap}.QueryContext(ctx, query, args...) +} + +func (m *WrappedMap) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row { + return WrappedExecutor{sqlExecutor: m.dbMap}.QueryRowContext(ctx, query, args...) +} + +func (m *WrappedMap) SelectStr(ctx context.Context, query string, args ...interface{}) (string, error) { + return WrappedExecutor{sqlExecutor: m.dbMap}.SelectStr(ctx, query, args...) +} + +func (m *WrappedMap) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { + return WrappedExecutor{sqlExecutor: m.dbMap}.ExecContext(ctx, query, args...) +} + +func (m *WrappedMap) BeginTx(ctx context.Context) (Transaction, error) { + tx, err := m.dbMap.BeginTx(ctx) if err != nil { return tx, ErrDatabaseOp{ Op: "begin transaction", @@ -108,66 +133,62 @@ func (m *WrappedMap) Begin() (Transaction, error) { } } return WrappedTransaction{ - Transaction: tx, + transaction: tx, }, err } -// WrappedTransaction wraps a *gorp.Transaction such that its major functions +// WrappedTransaction wraps a *borp.Transaction such that its major functions // wrap error results in ErrDatabaseOp instances before returning them to the // caller. type WrappedTransaction struct { - *gorp.Transaction -} - -func (tx WrappedTransaction) WithContext(ctx context.Context) gorp.SqlExecutor { - return WrappedExecutor{SqlExecutor: tx.Transaction.WithContext(ctx)} + transaction *borp.Transaction } func (tx WrappedTransaction) Commit() error { - return tx.Transaction.Commit() + return tx.transaction.Commit() } func (tx WrappedTransaction) Rollback() error { - return tx.Transaction.Rollback() + return tx.transaction.Rollback() } -func (tx WrappedTransaction) Get(holder interface{}, keys ...interface{}) (interface{}, error) { - return (WrappedExecutor{SqlExecutor: tx.Transaction}).Get(holder, keys...) +func (tx WrappedTransaction) Get(ctx context.Context, holder interface{}, keys ...interface{}) (interface{}, error) { + return (WrappedExecutor{sqlExecutor: tx.transaction}).Get(ctx, holder, keys...) } -func (tx WrappedTransaction) Insert(list ...interface{}) error { - return (WrappedExecutor{SqlExecutor: tx.Transaction}).Insert(list...) +func (tx WrappedTransaction) Insert(ctx context.Context, list ...interface{}) error { + return (WrappedExecutor{sqlExecutor: tx.transaction}).Insert(ctx, list...) } -func (tx WrappedTransaction) Update(list ...interface{}) (int64, error) { - return (WrappedExecutor{SqlExecutor: tx.Transaction}).Update(list...) +func (tx WrappedTransaction) Update(ctx context.Context, list ...interface{}) (int64, error) { + return (WrappedExecutor{sqlExecutor: tx.transaction}).Update(ctx, list...) } -func (tx WrappedTransaction) Delete(list ...interface{}) (int64, error) { - return (WrappedExecutor{SqlExecutor: tx.Transaction}).Delete(list...) +func (tx WrappedTransaction) Delete(ctx context.Context, list ...interface{}) (int64, error) { + return (WrappedExecutor{sqlExecutor: tx.transaction}).Delete(ctx, list...) } -func (tx WrappedTransaction) Select(holder interface{}, query string, args ...interface{}) ([]interface{}, error) { - return (WrappedExecutor{SqlExecutor: tx.Transaction}).Select(holder, query, args...) +func (tx WrappedTransaction) Select(ctx context.Context, holder interface{}, query string, args ...interface{}) ([]interface{}, error) { + return (WrappedExecutor{sqlExecutor: tx.transaction}).Select(ctx, holder, query, args...) } -func (tx WrappedTransaction) SelectOne(holder interface{}, query string, args ...interface{}) error { - return (WrappedExecutor{SqlExecutor: tx.Transaction}).SelectOne(holder, query, args...) +func (tx WrappedTransaction) SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error { + return (WrappedExecutor{sqlExecutor: tx.transaction}).SelectOne(ctx, holder, query, args...) } -func (tx WrappedTransaction) Query(query string, args ...interface{}) (*sql.Rows, error) { - return (WrappedExecutor{SqlExecutor: tx.Transaction}).Query(query, args...) +func (tx WrappedTransaction) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { + return (WrappedExecutor{sqlExecutor: tx.transaction}).QueryContext(ctx, query, args...) } -func (tx WrappedTransaction) Exec(query string, args ...interface{}) (sql.Result, error) { - return (WrappedExecutor{SqlExecutor: tx.Transaction}).Exec(query, args...) +func (tx WrappedTransaction) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { + return (WrappedExecutor{sqlExecutor: tx.transaction}).ExecContext(ctx, query, args...) } -// WrappedExecutor wraps a gorp.SqlExecutor such that its major functions +// WrappedExecutor wraps a borp.SqlExecutor such that its major functions // wrap error results in ErrDatabaseOp instances before returning them to the // caller. type WrappedExecutor struct { - gorp.SqlExecutor + sqlExecutor borp.SqlExecutor } func errForOp(operation string, err error, list []interface{}) ErrDatabaseOp { @@ -203,56 +224,78 @@ func errForQuery(query, operation string, err error, list []interface{}) ErrData } } -func (we WrappedExecutor) Get(holder interface{}, keys ...interface{}) (interface{}, error) { - res, err := we.SqlExecutor.Get(holder, keys...) +func (we WrappedExecutor) Get(ctx context.Context, holder interface{}, keys ...interface{}) (interface{}, error) { + res, err := we.sqlExecutor.Get(ctx, holder, keys...) if err != nil { return res, errForOp("get", err, []interface{}{holder}) } return res, err } -func (we WrappedExecutor) Insert(list ...interface{}) error { - err := we.SqlExecutor.Insert(list...) +func (we WrappedExecutor) Insert(ctx context.Context, list ...interface{}) error { + err := we.sqlExecutor.Insert(ctx, list...) if err != nil { return errForOp("insert", err, list) } return nil } -func (we WrappedExecutor) Update(list ...interface{}) (int64, error) { - updatedRows, err := we.SqlExecutor.Update(list...) +func (we WrappedExecutor) Update(ctx context.Context, list ...interface{}) (int64, error) { + updatedRows, err := we.sqlExecutor.Update(ctx, list...) if err != nil { return updatedRows, errForOp("update", err, list) } return updatedRows, err } -func (we WrappedExecutor) Delete(list ...interface{}) (int64, error) { - deletedRows, err := we.SqlExecutor.Delete(list...) +func (we WrappedExecutor) Delete(ctx context.Context, list ...interface{}) (int64, error) { + deletedRows, err := we.sqlExecutor.Delete(ctx, list...) if err != nil { return deletedRows, errForOp("delete", err, list) } return deletedRows, err } -func (we WrappedExecutor) Select(holder interface{}, query string, args ...interface{}) ([]interface{}, error) { - result, err := we.SqlExecutor.Select(holder, query, args...) +func (we WrappedExecutor) Select(ctx context.Context, holder interface{}, query string, args ...interface{}) ([]interface{}, error) { + result, err := we.sqlExecutor.Select(ctx, holder, query, args...) if err != nil { return result, errForQuery(query, "select", err, []interface{}{holder}) } return result, err } -func (we WrappedExecutor) SelectOne(holder interface{}, query string, args ...interface{}) error { - err := we.SqlExecutor.SelectOne(holder, query, args...) +func (we WrappedExecutor) SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error { + err := we.sqlExecutor.SelectOne(ctx, holder, query, args...) if err != nil { return errForQuery(query, "select one", err, []interface{}{holder}) } return nil } -func (we WrappedExecutor) Query(query string, args ...interface{}) (*sql.Rows, error) { - rows, err := we.SqlExecutor.Query(query, args...) +func (we WrappedExecutor) SelectNullInt(ctx context.Context, query string, args ...interface{}) (sql.NullInt64, error) { + rows, err := we.sqlExecutor.SelectNullInt(ctx, query, args...) + if err != nil { + return sql.NullInt64{}, errForQuery(query, "select", err, nil) + } + return rows, nil +} + +func (we WrappedExecutor) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row { + // Note: we can't do error wrapping here because the error is passed via the `*sql.Row` + // object, and we can't produce a `*sql.Row` object with a custom error because it is unexported. + return we.sqlExecutor.QueryRowContext(ctx, query, args...) +} + +func (we WrappedExecutor) SelectStr(ctx context.Context, query string, args ...interface{}) (string, error) { + str, err := we.sqlExecutor.SelectStr(ctx, query, args...) + if err != nil { + return "", errForQuery(query, "select", err, nil) + } + return str, nil +} + +func (we WrappedExecutor) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { + rows, err := we.sqlExecutor.QueryContext(ctx, query, args...) if err != nil { return nil, errForQuery(query, "select", err, nil) } @@ -271,10 +314,10 @@ var ( // tableRegexps is a list of regexps that tableFromQuery will try to use in // succession to find the table name for an SQL query. While tableFromQuery - // isn't used by the higher level gorp Insert/Update/Select/etc functions we + // isn't used by the higher level borp Insert/Update/Select/etc functions we // include regexps for matching inserts, updates, selects, etc because we want // to match the correct table when these types of queries are run through - // Exec(). + // ExecContext(). tableRegexps = []*regexp.Regexp{ selectTableRegexp, insertTableRegexp, @@ -295,8 +338,8 @@ func tableFromQuery(query string) string { return "" } -func (we WrappedExecutor) Exec(query string, args ...interface{}) (sql.Result, error) { - res, err := we.SqlExecutor.Exec(query, args...) +func (we WrappedExecutor) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { + res, err := we.sqlExecutor.ExecContext(ctx, query, args...) if err != nil { return res, errForQuery(query, "exec", err, args) } diff --git a/db/map_test.go b/db/map_test.go index a7ab2fe55..5ac548dab 100644 --- a/db/map_test.go +++ b/db/map_test.go @@ -7,7 +7,7 @@ import ( "fmt" "testing" - gorp "github.com/go-gorp/gorp/v3" + "github.com/letsencrypt/borp" "github.com/go-sql-driver/mysql" "github.com/letsencrypt/boulder/core" @@ -236,25 +236,28 @@ func testDbMap(t *testing.T) *WrappedMap { dbConn, err := sql.Open("mysql", config.FormatDSN()) test.AssertNotError(t, err, "opening DB connection") - dialect := gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"} + dialect := borp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"} // NOTE(@cpu): We avoid giving a sa.BoulderTypeConverter to the DbMap field to // avoid the cyclic dep. We don't need to convert any types in the db tests. - dbMap := &gorp.DbMap{Db: dbConn, Dialect: dialect, TypeConverter: nil} - return &WrappedMap{DbMap: dbMap} + dbMap := &borp.DbMap{Db: dbConn, Dialect: dialect, TypeConverter: nil} + return &WrappedMap{dbMap: dbMap} } func TestWrappedMap(t *testing.T) { mustDbErr := func(err error) ErrDatabaseOp { + t.Helper() var dbOpErr ErrDatabaseOp test.AssertErrorWraps(t, err, &dbOpErr) return dbOpErr } + ctx := context.Background() + testWrapper := func(dbMap Executor) { reg := &core.Registration{} // Test wrapped Get - _, err := dbMap.Get(reg) + _, err := dbMap.Get(ctx, reg) test.AssertError(t, err, "expected err Getting Registration w/o type converter") dbOpErr := mustDbErr(err) test.AssertEquals(t, dbOpErr.Op, "get") @@ -262,7 +265,7 @@ func TestWrappedMap(t *testing.T) { test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err") // Test wrapped Insert - err = dbMap.Insert(reg) + err = dbMap.Insert(ctx, reg) test.AssertError(t, err, "expected err Inserting Registration w/o type converter") dbOpErr = mustDbErr(err) test.AssertEquals(t, dbOpErr.Op, "insert") @@ -270,7 +273,7 @@ func TestWrappedMap(t *testing.T) { test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err") // Test wrapped Update - _, err = dbMap.Update(reg) + _, err = dbMap.Update(ctx, reg) test.AssertError(t, err, "expected err Updating Registration w/o type converter") dbOpErr = mustDbErr(err) test.AssertEquals(t, dbOpErr.Op, "update") @@ -278,7 +281,7 @@ func TestWrappedMap(t *testing.T) { test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err") // Test wrapped Delete - _, err = dbMap.Delete(reg) + _, err = dbMap.Delete(ctx, reg) test.AssertError(t, err, "expected err Deleting Registration w/o type converter") dbOpErr = mustDbErr(err) test.AssertEquals(t, dbOpErr.Op, "delete") @@ -286,7 +289,7 @@ func TestWrappedMap(t *testing.T) { test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err") // Test wrapped Select with a bogus query - _, err = dbMap.Select(reg, "blah") + _, err = dbMap.Select(ctx, reg, "blah") test.AssertError(t, err, "expected err Selecting Registration w/o type converter") dbOpErr = mustDbErr(err) test.AssertEquals(t, dbOpErr.Op, "select") @@ -294,7 +297,7 @@ func TestWrappedMap(t *testing.T) { test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err") // Test wrapped Select with a valid query - _, err = dbMap.Select(reg, "SELECT id, contact FROM registrationzzz WHERE id > 1;") + _, err = dbMap.Select(ctx, reg, "SELECT id, contact FROM registrationzzz WHERE id > 1;") test.AssertError(t, err, "expected err Selecting Registration w/o type converter") dbOpErr = mustDbErr(err) test.AssertEquals(t, dbOpErr.Op, "select") @@ -302,7 +305,7 @@ func TestWrappedMap(t *testing.T) { test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err") // Test wrapped SelectOne with a bogus query - err = dbMap.SelectOne(reg, "blah") + err = dbMap.SelectOne(ctx, reg, "blah") test.AssertError(t, err, "expected err SelectOne-ing Registration w/o type converter") dbOpErr = mustDbErr(err) test.AssertEquals(t, dbOpErr.Op, "select one") @@ -310,7 +313,7 @@ func TestWrappedMap(t *testing.T) { test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err") // Test wrapped SelectOne with a valid query - err = dbMap.SelectOne(reg, "SELECT contact FROM doesNotExist WHERE id=1;") + err = dbMap.SelectOne(ctx, reg, "SELECT contact FROM doesNotExist WHERE id=1;") test.AssertError(t, err, "expected err SelectOne-ing Registration w/o type converter") dbOpErr = mustDbErr(err) test.AssertEquals(t, dbOpErr.Op, "select one") @@ -318,7 +321,7 @@ func TestWrappedMap(t *testing.T) { test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err") // Test wrapped Exec - _, err = dbMap.Exec("INSERT INTO whatever (id) VALUES (?) WHERE id = ?", 10) + _, err = dbMap.ExecContext(ctx, "INSERT INTO whatever (id) VALUES (?) WHERE id = ?", 10) test.AssertError(t, err, "expected err Exec-ing bad query") dbOpErr = mustDbErr(err) test.AssertEquals(t, dbOpErr.Op, "exec") @@ -333,24 +336,10 @@ func TestWrappedMap(t *testing.T) { // database errors. testWrapper(dbMap) - // Using WithContext on the WrappedMap should return a map that continues to - // operate in the expected fashion. - dbMapWithCtx := dbMap.WithContext(context.Background()) - testWrapper(dbMapWithCtx) - // Using Begin to start a transaction with the dbMap should return a // transaction that continues to operate in the expected fashion. - tx, err := dbMap.Begin() + tx, err := dbMap.BeginTx(ctx) defer func() { _ = tx.Rollback() }() test.AssertNotError(t, err, "unexpected error beginning transaction") testWrapper(tx) - - // Using Begin to start a transaction with the dbMap and then using - // WithContext should return a transaction that continues to operate in the - // expected fashion. - tx, err = dbMap.Begin() - defer func() { _ = tx.Rollback() }() - test.AssertNotError(t, err, "unexpected error beginning transaction") - txWithContext := tx.WithContext(context.Background()) - testWrapper(txWithContext) } diff --git a/db/multi.go b/db/multi.go index eee42411f..bcb2fbe3f 100644 --- a/db/multi.go +++ b/db/multi.go @@ -1,6 +1,7 @@ package db import ( + "context" "fmt" "strings" ) @@ -68,7 +69,7 @@ func (mi *MultiInserter) Add(row []interface{}) error { } // query returns the formatted query string, and the slice of arguments for -// for gorp to use in place of the query's question marks. Currently only +// for borp to use in place of the query's question marks. Currently only // used by .Insert(), below. func (mi *MultiInserter) query() (string, []interface{}) { var questionsBuf strings.Builder @@ -101,12 +102,11 @@ func (mi *MultiInserter) query() (string, []interface{}) { } // Insert inserts all the collected rows into the database represented by -// `queryer`. `queryer` is assumed to already have a context attached. If a -// non-empty returningColumn was provided, then it returns the list of values -// from that column returned by the query. -func (mi *MultiInserter) Insert(queryer Queryer) ([]int64, error) { +// `queryer`. If a non-empty returningColumn was provided, then it returns +// the list of values from that column returned by the query. +func (mi *MultiInserter) Insert(ctx context.Context, queryer Queryer) ([]int64, error) { query, queryArgs := mi.query() - rows, err := queryer.Query(query, queryArgs...) + rows, err := queryer.QueryContext(ctx, query, queryArgs...) if err != nil { return nil, err } diff --git a/db/rollback_test.go b/db/rollback_test.go index 7dcf48b80..99df5431c 100644 --- a/db/rollback_test.go +++ b/db/rollback_test.go @@ -1,6 +1,7 @@ package db import ( + "context" "testing" berrors "github.com/letsencrypt/boulder/errors" @@ -8,9 +9,10 @@ import ( ) func TestRollback(t *testing.T) { + ctx := context.Background() dbMap := testDbMap(t) - tx, _ := dbMap.Begin() + tx, _ := dbMap.BeginTx(ctx) // Commit the transaction so that a subsequent rollback will always fail. _ = tx.Commit() @@ -28,7 +30,7 @@ func TestRollback(t *testing.T) { // Create a new transaction and don't commit it this time. The rollback should // succeed. - tx, _ = dbMap.Begin() + tx, _ = dbMap.BeginTx(ctx) result = rollback(tx, innerErr) // We expect that the err is returned unwrapped. diff --git a/db/transaction.go b/db/transaction.go index 1bd302916..f6020962f 100644 --- a/db/transaction.go +++ b/db/transaction.go @@ -3,19 +3,18 @@ package db import "context" // txFunc represents a function that does work in the context of a transaction. -type txFunc func(txWithCtx Executor) (interface{}, error) +type txFunc func(tx Executor) (interface{}, error) // WithTransaction runs the given function in a transaction, rolling back if it // returns an error and committing if not. The provided context is also attached // to the transaction. WithTransaction also passes through a value returned by // `f`, if there is no error. func WithTransaction(ctx context.Context, dbMap DatabaseMap, f txFunc) (interface{}, error) { - tx, err := dbMap.Begin() + tx, err := dbMap.BeginTx(ctx) if err != nil { return nil, err } - txWithCtx := tx.WithContext(ctx) - result, err := f(txWithCtx) + result, err := f(tx) if err != nil { return nil, rollback(tx, err) } diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index c6009de85..7512e4e62 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -191,10 +191,10 @@ Specifically, that means that all of our `SELECT` statements should enumerate columns to select, and not use `*`. Also, generally speaking, we will need a separate model `struct` for serializing and deserializing data before and after the migration. This is because the ORM package we use, -[`gorp`](https://github.com/go-gorp/gorp), expects every field in a struct to +[`borp`](https://github.com/letsencrypt/borp), expects every field in a struct to map to a column in the table. If we add a new field to a model struct and Boulder attempts to write that struct to a table that doesn't yet have the -corresponding column (case 1), gorp will fail with `Insert failed table posts +corresponding column (case 1), borp will fail with `Insert failed table posts has no column named Foo`. There are examples of such models in sa/model.go, along with code to turn a model into a `struct` used internally. @@ -254,14 +254,14 @@ func (ssa *SQLStorageAuthority) GetPerson() (Person, error) { func (ssa *SQLStorageAuthority) AddPerson(p Person) (error) { if features.Enabled(features.AllowWizards) { // Added! - return ssa.dbMap.Insert(personModelv2{ + return ssa.dbMap.Insert(context.Background(), personModelv2{ personModelv1: { HatSize: p.HatSize, }, IsWizard: p.IsWizard, }) } else { - return ssa.dbMap.Insert(personModelv1{ + return ssa.dbMap.Insert(context.Background(), personModelv1{ HatSize: p.HatSize, // p.IsWizard ignored }) @@ -270,14 +270,14 @@ func (ssa *SQLStorageAuthority) AddPerson(p Person) (error) { ``` You will also need to update the `initTables` function from `sa/database.go` to -tell Gorp which table to use for your versioned model structs. Make sure to +tell borp which table to use for your versioned model structs. Make sure to consult the flag you defined so that only **one** of the table maps is added at -any given time, otherwise Gorp will error. Depending on your table you may also +any given time, otherwise borp will error. Depending on your table you may also need to add `SetKeys` and `SetVersionCol` entries for your versioned models. Example: ```go -func initTables(dbMap *gorp.DbMap) { +func initTables(dbMap *borp.DbMap) { // < unrelated lines snipped for brevity > if features.Enabled(features.AllowWizards) { diff --git a/go.mod b/go.mod index e4568c12b..a377637d2 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ require ( github.com/aws/smithy-go v1.13.5 github.com/beeker1121/goque v1.0.3-0.20191103205551-d618510128af github.com/eggsampler/acme/v3 v3.4.0 - github.com/go-gorp/gorp/v3 v3.1.0 github.com/go-logr/stdr v1.2.2 github.com/go-redis/redis/v8 v8.11.5 github.com/go-sql-driver/mysql v1.7.1 @@ -18,6 +17,7 @@ require ( github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/hpcloud/tail v1.0.0 github.com/jmhodges/clock v1.2.0 + github.com/letsencrypt/borp v0.0.0-20230707160741-6cc6ce580243 github.com/letsencrypt/challtestsrv v1.2.1 github.com/letsencrypt/pkcs11key/v4 v4.0.0 github.com/letsencrypt/validator/v10 v10.0.0-20230215210743-a0c7dfc17158 @@ -73,11 +73,12 @@ require ( github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/pelletier/go-toml v1.9.3 // indirect + github.com/poy/onpar v1.1.2 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/syndtr/goleveldb v1.0.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.15.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.15.0 // indirect @@ -87,6 +88,7 @@ require ( golang.org/x/sys v0.9.0 // indirect golang.org/x/tools v0.8.0 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/fsnotify.v1 v1.4.7 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect k8s.io/klog/v2 v2.80.1 // indirect diff --git a/go.sum b/go.sum index 985023701..eb38cea04 100644 --- a/go.sum +++ b/go.sum @@ -39,12 +39,14 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= +github.com/a8m/expect v1.0.0/go.mod h1:4IwSCMumY49ScypDnjNbYEjgVeqy1/U2cEs3Lat96eA= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.18.1 h1:+tefE750oAb7ZQGzla6bLkOwfcQCEtC5y2RqoqCeqKo= @@ -113,11 +115,20 @@ github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/eggsampler/acme/v3 v3.4.0 h1:LHWnB3wShVshK1+umL6ObCjnc0MM+D7TE8JINjk8zGY= github.com/eggsampler/acme/v3 v3.4.0/go.mod h1:/qh0rKC/Dh7Jj+p4So7DbWmFNzC4dpcpK53r226Fhuo= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -127,6 +138,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -135,8 +147,6 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= -github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -162,9 +172,11 @@ github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrt github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -233,21 +245,27 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 h1:gDLXvp5S9izjldquuoAhDzccbskOL6tDC5jMSyx3zxE= github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2/go.mod h1:7pdNwVWBBHGiCxa9lAszqCJMbfTISJ7oMftp8+UGV08= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs= github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -257,23 +275,32 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/letsencrypt/borp v0.0.0-20230707160741-6cc6ce580243 h1:xS2U6PQYRURk61YN4Y5xvyLbQVyAP/8fpE6hJZdwEWs= +github.com/letsencrypt/borp v0.0.0-20230707160741-6cc6ce580243/go.mod h1:podMDq5wDu2ZO6JMKYQcjD3QdqOfNLWtP2RDSy8CHUU= github.com/letsencrypt/challtestsrv v1.2.1 h1:Lzv4jM+wSgVMCeO5a/F/IzSanhClstFMnX6SfrAJXjI= github.com/letsencrypt/challtestsrv v1.2.1/go.mod h1:Ur4e4FvELUXLGhkMztHOsPIsvGxD/kzSJninOrkM+zc= github.com/letsencrypt/pkcs11key/v4 v4.0.0 h1:qLc/OznH7xMr5ARJgkZCCWk+EomQkiNTOoOF5LAgagc= github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= github.com/letsencrypt/validator/v10 v10.0.0-20230215210743-a0c7dfc17158 h1:HGFsIltYMUiB5eoFSowFzSoXkocM2k9ctmJ57QMGjys= github.com/letsencrypt/validator/v10 v10.0.0-20230215210743-a0c7dfc17158/go.mod h1:ZFNBS3H6OEsprCRjscty6GCBe5ZiX44x6qY4s7+bDX0= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= @@ -282,6 +309,8 @@ github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7Xn github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -291,22 +320,29 @@ github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1 github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nelsam/hel/v2 v2.3.2/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/poy/onpar v0.0.0-20200406201722-06f95a1c68e8/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= +github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= @@ -319,6 +355,8 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= @@ -326,23 +364,35 @@ github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+ github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +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.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -350,17 +400,21 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= 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/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/weppos/publicsuffix-go v0.12.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k= github.com/weppos/publicsuffix-go v0.13.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k= github.com/weppos/publicsuffix-go v0.30.0/go.mod h1:kBi8zwYnR0zrbm8RcuN1o9Fzgpnnn+btVN8uWPMyXAY= github.com/weppos/publicsuffix-go v0.30.1-0.20230620154423-38c92ad2d5c6 h1:kNn7cjQYeNjKUflvFFCxFeyS7ENcDdfPmkhFpgd0G/A= github.com/weppos/publicsuffix-go v0.30.1-0.20230620154423-38c92ad2d5c6/go.mod h1:wdMq89hDN07Zqr0yqYAXIBTJXl4MEELx+HYHOZdf5gM= github.com/weppos/publicsuffix-go/publicsuffix/generator v0.0.0-20220927085643-dc0d00c92642/go.mod h1:GHfoeIdZLdZmLjMlzBftbTDntahTttUMWjxZwQJhULE= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -377,6 +431,7 @@ github.com/zmap/zcrypto v0.0.0-20230310154051-c8b263fd8300/go.mod h1:mOd4yUMgn2f github.com/zmap/zlint/v3 v3.0.0/go.mod h1:paGwFySdHIBEMJ61YjoqT4h7Ge+fdYG4sUQhnTb1lJ8= github.com/zmap/zlint/v3 v3.5.0 h1:Eh2B5t6VKgVH0DFmTwOqE50POvyDhUaU9T2mJOe1vfQ= github.com/zmap/zlint/v3 v3.5.0/go.mod h1:JkNSrsDJ8F4VRtBZcYUQSvnWFL7utcjDIn+FE64mlBI= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -403,7 +458,10 @@ go.opentelemetry.io/otel/trace v1.15.0/go.mod h1:CUsmE2Ht1CRkvE8OsMESvraoZrrcgD1 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -457,12 +515,14 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -523,8 +583,10 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -600,6 +662,7 @@ golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -632,6 +695,7 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200313205530-4303120df7d8/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -708,6 +772,7 @@ google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= @@ -746,13 +811,16 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-jose/go-jose.v2 v2.6.1 h1:qEzJlIDmG9q5VO0M/o8tGS65QMHMS1w01TQJB1VPJ4U= gopkg.in/go-jose/go-jose.v2 v2.6.1/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/ocsp/responder/redis/checked_redis_source.go b/ocsp/responder/redis/checked_redis_source.go index e1d1b62aa..08186d7b4 100644 --- a/ocsp/responder/redis/checked_redis_source.go +++ b/ocsp/responder/redis/checked_redis_source.go @@ -6,7 +6,6 @@ import ( "reflect" "sync" - "github.com/go-gorp/gorp/v3" "github.com/prometheus/client_golang/prometheus" "golang.org/x/crypto/ocsp" @@ -22,8 +21,7 @@ import ( // dbSelector is a limited subset of the db.WrappedMap interface to allow for // easier mocking of mysql operations in tests. type dbSelector interface { - SelectOne(holder interface{}, query string, args ...interface{}) error - WithContext(ctx context.Context) gorp.SqlExecutor + SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error } // rocspSourceInterface expands on responder.Source by adding a private signAndSave method. @@ -102,7 +100,7 @@ func (src *checkedRedisSource) Response(ctx context.Context, req *ocsp.Request) if src.sac != nil { dbStatus, dbErr = src.sac.GetRevocationStatus(ctx, &sapb.Serial{Serial: serialString}) } else { - dbStatus, dbErr = sa.SelectRevocationStatus(src.dbMap.WithContext(ctx), serialString) + dbStatus, dbErr = sa.SelectRevocationStatus(ctx, src.dbMap, serialString) } }() go func() { diff --git a/ocsp/responder/redis/checked_redis_source_test.go b/ocsp/responder/redis/checked_redis_source_test.go index 999798fc0..2075864bd 100644 --- a/ocsp/responder/redis/checked_redis_source_test.go +++ b/ocsp/responder/redis/checked_redis_source_test.go @@ -9,7 +9,6 @@ import ( "testing" "time" - "github.com/go-gorp/gorp/v3" "golang.org/x/crypto/ocsp" "google.golang.org/grpc" "google.golang.org/protobuf/types/known/timestamppb" @@ -71,11 +70,7 @@ type echoSelector struct { status sa.RevocationStatusModel } -func (s echoSelector) WithContext(context.Context) gorp.SqlExecutor { - return s -} - -func (s echoSelector) SelectOne(output interface{}, _ string, _ ...interface{}) error { +func (s echoSelector) SelectOne(_ context.Context, output interface{}, _ string, _ ...interface{}) error { outputPtr, ok := output.(*sa.RevocationStatusModel) if !ok { return fmt.Errorf("incorrect output type %T", output) @@ -89,27 +84,19 @@ type errorSelector struct { db.MockSqlExecutor } -func (s errorSelector) SelectOne(_ interface{}, _ string, _ ...interface{}) error { +func (s errorSelector) SelectOne(_ context.Context, _ interface{}, _ string, _ ...interface{}) error { return errors.New("oops") } -func (s errorSelector) WithContext(context.Context) gorp.SqlExecutor { - return s -} - // notFoundSelector always returns an NoRows error. type notFoundSelector struct { db.MockSqlExecutor } -func (s notFoundSelector) SelectOne(_ interface{}, _ string, _ ...interface{}) error { +func (s notFoundSelector) SelectOne(_ context.Context, _ interface{}, _ string, _ ...interface{}) error { return db.ErrDatabaseOp{Err: sql.ErrNoRows} } -func (s notFoundSelector) WithContext(context.Context) gorp.SqlExecutor { - return s -} - // echoSA always returns the given revocation status. type echoSA struct { mocks.StorageAuthorityReadOnly diff --git a/sa/database.go b/sa/database.go index d0a04da2f..254059f0f 100644 --- a/sa/database.go +++ b/sa/database.go @@ -5,8 +5,8 @@ import ( "fmt" "time" - "github.com/go-gorp/gorp/v3" "github.com/go-sql-driver/mysql" + "github.com/letsencrypt/borp" "github.com/prometheus/client_golang/prometheus" "github.com/letsencrypt/boulder/cmd" @@ -45,7 +45,7 @@ type DbSettings struct { ConnMaxIdleTime time.Duration } -// InitWrappedDb constructs a wrapped gorp mapping object with the provided +// InitWrappedDb constructs a wrapped borp mapping object with the provided // settings. If scope is non-nil, Prometheus metrics will be exported. If logger // is non-nil, SQL debug-level logging will be enabled. The only required parameter // is config. @@ -82,7 +82,7 @@ func InitWrappedDb(config cmd.DBConfig, scope prometheus.Registerer, logger blog } if scope != nil { - err = initDBMetrics(dbMap.Db, scope, settings, addr, user) + err = initDBMetrics(dbMap.SQLDb(), scope, settings, addr, user) if err != nil { return nil, fmt.Errorf("while initializing metrics: %w", err) } @@ -90,7 +90,7 @@ func InitWrappedDb(config cmd.DBConfig, scope prometheus.Registerer, logger blog return dbMap, nil } -// DBMapForTest creates a wrapped root gorp mapping object. Create one of these for +// DBMapForTest creates a wrapped root borp mapping object. Create one of these for // each database schema you wish to map. Each DbMap contains a list of mapped // tables. It automatically maps the tables for the primary parts of Boulder // around the Storage Authority. @@ -146,7 +146,7 @@ var setConnMaxIdleTime = func(db *sql.DB, connMaxIdleTime time.Duration) { // // This function also: // - pings the database (and errors if it's unreachable) -// - wraps the connection in a gorp.DbMap so we can use the handy Get/Insert methods gorp provides +// - wraps the connection in a borp.DbMap so we can use the handy Get/Insert methods borp provides // - wraps that in a db.WrappedMap to get more useful error messages func newDbMapFromMySQLConfig(config *mysql.Config, settings DbSettings) (*boulderDB.WrappedMap, error) { err := adjustMySQLConfig(config) @@ -166,11 +166,11 @@ func newDbMapFromMySQLConfig(config *mysql.Config, settings DbSettings) (*boulde setConnMaxLifetime(db, settings.ConnMaxLifetime) setConnMaxIdleTime(db, settings.ConnMaxIdleTime) - dialect := gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"} - dbmap := &gorp.DbMap{Db: db, Dialect: dialect, TypeConverter: BoulderTypeConverter{}} + dialect := borp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"} + dbmap := &borp.DbMap{Db: db, Dialect: dialect, TypeConverter: BoulderTypeConverter{}} initTables(dbmap) - return &boulderDB.WrappedMap{DbMap: dbmap}, nil + return boulderDB.NewWrappedMap(dbmap), nil } // adjustMySQLConfig sets certain flags that we want on every connection. @@ -239,17 +239,17 @@ func adjustMySQLConfig(conf *mysql.Config) error { return nil } -// SetSQLDebug enables GORP SQL-level Debugging +// SetSQLDebug enables borp SQL-level Debugging func SetSQLDebug(dbMap *boulderDB.WrappedMap, log blog.Logger) { - dbMap.TraceOn("SQL: ", &SQLLogger{log}) + dbMap.BorpDB().TraceOn("SQL: ", &SQLLogger{log}) } -// SQLLogger adapts the Boulder Logger to a format GORP can use. +// SQLLogger adapts the Boulder Logger to a format borp can use. type SQLLogger struct { blog.Logger } -// Printf adapts the AuditLogger to GORP's interface +// Printf adapts the AuditLogger to borp's interface func (log *SQLLogger) Printf(format string, v ...interface{}) { log.Debugf(format, v...) } @@ -259,8 +259,8 @@ func (log *SQLLogger) Printf(format string, v ...interface{}) { // it is very important to declare them as a such here. It produces a side // effect in Insert() where the inserted object has its id field set to the // autoincremented value that resulted from the insert. See -// https://godoc.org/github.com/coopernurse/gorp#DbMap.Insert -func initTables(dbMap *gorp.DbMap) { +// https://godoc.org/github.com/coopernurse/borp#DbMap.Insert +func initTables(dbMap *borp.DbMap) { regTable := dbMap.AddTableWithName(regModel{}, "registrations").SetKeys(true, "ID") regTable.SetVersionCol("LockCol") diff --git a/sa/database_test.go b/sa/database_test.go index a5a3761ea..400e02c90 100644 --- a/sa/database_test.go +++ b/sa/database_test.go @@ -1,6 +1,7 @@ package sa import ( + "context" "database/sql" "errors" "os" @@ -134,7 +135,7 @@ func TestStrictness(t *testing.T) { if err != nil { t.Fatal(err) } - _, err = dbMap.Exec(`insert into orderToAuthz2 set + _, err = dbMap.ExecContext(ctx, `insert into orderToAuthz2 set orderID=999999999999999999999999999, authzID=999999999999999999999999999;`) if err == nil { @@ -153,7 +154,7 @@ func TestTimeouts(t *testing.T) { // SLEEP is defined to return 1 if it was interrupted, but we want to actually // get an error to simulate what would happen with a slow query. So we wrap // the SLEEP in a subselect. - _, err = dbMap.Exec(`SELECT 1 FROM (SELECT SLEEP(5)) as subselect;`) + _, err = dbMap.ExecContext(ctx, `SELECT 1 FROM (SELECT SLEEP(5)) as subselect;`) if err == nil { t.Fatal("Expected error when running slow query, got none.") } @@ -175,6 +176,7 @@ func TestAutoIncrementSchema(t *testing.T) { var count int64 err = dbMap.SelectOne( + context.Background(), &count, `SELECT COUNT(*) FROM columns WHERE table_schema LIKE 'boulder%' AND diff --git a/sa/model.go b/sa/model.go index c979dcb2e..695539cb7 100644 --- a/sa/model.go +++ b/sa/model.go @@ -64,9 +64,9 @@ const regFields = "id, jwk, jwk_sha256, contact, agreement, initialIP, createdAt // ClearEmail removes the provided email address from one specified registration. If // there are multiple email addresses present, it does not modify other ones. If the email // address is not present, it does not modify the registration and will return a nil error. -func ClearEmail(dbMap db.DatabaseMap, ctx context.Context, regID int64, email string) error { - _, overallError := db.WithTransaction(ctx, dbMap, func(txWithCtx db.Executor) (interface{}, error) { - curr, err := selectRegistration(txWithCtx, "id", regID) +func ClearEmail(ctx context.Context, dbMap db.DatabaseMap, regID int64, email string) error { + _, overallError := db.WithTransaction(ctx, dbMap, func(tx db.Executor) (interface{}, error) { + curr, err := selectRegistration(ctx, tx, "id", regID) if err != nil { return nil, err } @@ -94,7 +94,7 @@ func ClearEmail(dbMap db.DatabaseMap, ctx context.Context, regID int64, email st return nil, err } - return txWithCtx.Update(newModel) + return tx.Update(ctx, newModel) }) if overallError != nil { return overallError @@ -104,13 +104,14 @@ func ClearEmail(dbMap db.DatabaseMap, ctx context.Context, regID int64, email st } // selectRegistration selects all fields of one registration model -func selectRegistration(s db.OneSelector, whereCol string, args ...interface{}) (*regModel, error) { +func selectRegistration(ctx context.Context, s db.OneSelector, whereCol string, args ...interface{}) (*regModel, error) { if whereCol != "id" && whereCol != "jwk_sha256" { return nil, fmt.Errorf("column name %q invalid for registrations table WHERE clause", whereCol) } var model regModel err := s.SelectOne( + ctx, &model, "SELECT "+regFields+" FROM registrations WHERE "+whereCol+" = ? LIMIT 1", args..., @@ -123,9 +124,10 @@ const certFields = "registrationID, serial, digest, der, issued, expires" // SelectCertificate selects all fields of one certificate object identified by // a serial. If more than one row contains the same serial only the first is // returned. -func SelectCertificate(s db.OneSelector, serial string) (core.Certificate, error) { +func SelectCertificate(ctx context.Context, s db.OneSelector, serial string) (core.Certificate, error) { var model core.Certificate err := s.SelectOne( + ctx, &model, "SELECT "+certFields+" FROM certificates WHERE serial = ? LIMIT 1", serial, @@ -137,9 +139,10 @@ const precertFields = "registrationID, serial, der, issued, expires" // SelectPrecertificate selects all fields of one precertificate object // identified by serial. -func SelectPrecertificate(s db.OneSelector, serial string) (core.Certificate, error) { +func SelectPrecertificate(ctx context.Context, s db.OneSelector, serial string) (core.Certificate, error) { var model precertificateModel err := s.SelectOne( + ctx, &model, "SELECT "+precertFields+" FROM precertificates WHERE serial = ? LIMIT 1", serial) @@ -158,18 +161,20 @@ type CertWithID struct { } // SelectCertificates selects all fields of multiple certificate objects -func SelectCertificates(s db.Selector, q string, args map[string]interface{}) ([]CertWithID, error) { +func SelectCertificates(ctx context.Context, s db.Selector, q string, args map[string]interface{}) ([]CertWithID, error) { var models []CertWithID _, err := s.Select( + ctx, &models, "SELECT id, "+certFields+" FROM certificates "+q, args) return models, err } // SelectPrecertificates selects all fields of multiple precertificate objects. -func SelectPrecertificates(s db.Selector, q string, args map[string]interface{}) ([]CertWithID, error) { +func SelectPrecertificates(ctx context.Context, s db.Selector, q string, args map[string]interface{}) ([]CertWithID, error) { var models []CertWithID _, err := s.Select( + ctx, &models, "SELECT id, "+precertFields+" FROM precertificates "+q, args) return models, err @@ -192,9 +197,10 @@ const certStatusFields = "id, serial, status, ocspLastUpdated, revokedDate, revo // SelectCertificateStatus selects all fields of one certificate status model // identified by serial -func SelectCertificateStatus(s db.OneSelector, serial string) (core.CertificateStatus, error) { +func SelectCertificateStatus(ctx context.Context, s db.OneSelector, serial string) (core.CertificateStatus, error) { var model core.CertificateStatus err := s.SelectOne( + ctx, &model, "SELECT "+certStatusFields+" FROM certificateStatus WHERE serial = ? LIMIT 1", serial, @@ -213,9 +219,10 @@ type RevocationStatusModel struct { // SelectRevocationStatus returns the authoritative revocation information for // the certificate with the given serial. -func SelectRevocationStatus(s db.OneSelector, serial string) (*sapb.RevocationStatus, error) { +func SelectRevocationStatus(ctx context.Context, s db.OneSelector, serial string) (*sapb.RevocationStatus, error) { var model RevocationStatusModel err := s.SelectOne( + ctx, &model, "SELECT status, revokedDate, revokedReason FROM certificateStatus WHERE serial = ? LIMIT 1", serial, @@ -560,6 +567,7 @@ func rehydrateHostPort(vr *core.ValidationRecord) error { // the past. If the stored authz has a valid status, it is returned with a // valid status regardless of whether it is also expired. func SelectAuthzsMatchingIssuance( + ctx context.Context, s db.Selector, regID int64, issued time.Time, @@ -588,7 +596,7 @@ func SelectAuthzsMatchingIssuance( } var authzModels []authzModel - _, err := s.Select(&authzModels, query, args...) + _, err := s.Select(ctx, &authzModels, query, args...) if err != nil { return nil, err } @@ -885,8 +893,8 @@ type orderFQDNSet struct { Expires time.Time } -func addFQDNSet(db db.Inserter, names []string, serial string, issued time.Time, expires time.Time) error { - return db.Insert(&core.FQDNSet{ +func addFQDNSet(ctx context.Context, db db.Inserter, names []string, serial string, issued time.Time, expires time.Time) error { + return db.Insert(ctx, &core.FQDNSet{ SetHash: HashNames(names), Serial: serial, Issued: issued, @@ -899,12 +907,13 @@ func addFQDNSet(db db.Inserter, names []string, serial string, issued time.Time, // addition can take place within the order addition transaction. The caller is // required to rollback the transaction if an error is returned. func addOrderFQDNSet( + ctx context.Context, db db.Inserter, names []string, orderID int64, regID int64, expires time.Time) error { - return db.Insert(&orderFQDNSet{ + return db.Insert(ctx, &orderFQDNSet{ SetHash: HashNames(names), OrderID: orderID, RegistrationID: regID, @@ -917,10 +926,11 @@ func addOrderFQDNSet( // take place within the finalization transaction. The caller is required to // rollback the transaction if an error is returned. func deleteOrderFQDNSet( + ctx context.Context, db db.Execer, orderID int64) error { - result, err := db.Exec(` + result, err := db.ExecContext(ctx, ` DELETE FROM orderFqdnSets WHERE orderID = ?`, orderID) @@ -940,7 +950,7 @@ func deleteOrderFQDNSet( return nil } -func addIssuedNames(queryer db.Queryer, cert *x509.Certificate, isRenewal bool) error { +func addIssuedNames(ctx context.Context, queryer db.Queryer, cert *x509.Certificate, isRenewal bool) error { if len(cert.DNSNames) == 0 { return berrors.InternalServerError("certificate has no DNSNames") } @@ -960,11 +970,11 @@ func addIssuedNames(queryer db.Queryer, cert *x509.Certificate, isRenewal bool) return err } } - _, err = multiInserter.Insert(queryer) + _, err = multiInserter.Insert(ctx, queryer) return err } -func addKeyHash(db db.Inserter, cert *x509.Certificate) error { +func addKeyHash(ctx context.Context, db db.Inserter, cert *x509.Certificate) error { if cert.RawSubjectPublicKeyInfo == nil { return errors.New("certificate has a nil RawSubjectPublicKeyInfo") } @@ -974,7 +984,7 @@ func addKeyHash(db db.Inserter, cert *x509.Certificate) error { CertNotAfter: cert.NotAfter, CertSerial: core.SerialToString(cert.SerialNumber), } - return db.Insert(khm) + return db.Insert(ctx, khm) } var blockedKeysColumns = "keyHash, added, source, comment" @@ -992,9 +1002,8 @@ var blockedKeysColumns = "keyHash, added, source, comment" // - If all of the order's authorizations are valid, and we haven't begun // processing, then the order is status ready. // -// An error is returned for any other case. It assumes that the provided -// database selector already has a context associated with it. -func statusForOrder(s db.Selector, order *corepb.Order, now time.Time) (string, error) { +// An error is returned for any other case. +func statusForOrder(ctx context.Context, s db.Selector, order *corepb.Order, now time.Time) (string, error) { // Without any further work we know an order with an error is invalid if order.Error != nil { return string(core.StatusInvalid), nil @@ -1013,7 +1022,7 @@ func statusForOrder(s db.Selector, order *corepb.Order, now time.Time) (string, } // Get the full Authorization objects for the order - authzValidityInfo, err := getAuthorizationStatuses(s, order.V2Authorizations) + authzValidityInfo, err := getAuthorizationStatuses(ctx, s, order.V2Authorizations) // If there was an error getting the authorizations, return it immediately if err != nil { return "", err @@ -1109,9 +1118,8 @@ type authzValidity struct { } // getAuthorizationStatuses takes a sequence of authz IDs, and returns the -// status and expiration date of each of them. It assumes that the provided -// database selector already has a context associated with it. -func getAuthorizationStatuses(s db.Selector, ids []int64) ([]authzValidity, error) { +// status and expiration date of each of them. +func getAuthorizationStatuses(ctx context.Context, s db.Selector, ids []int64) ([]authzValidity, error) { var params []interface{} for _, id := range ids { params = append(params, id) @@ -1121,6 +1129,7 @@ func getAuthorizationStatuses(s db.Selector, ids []int64) ([]authzValidity, erro Expires time.Time } _, err := s.Select( + ctx, &validityInfo, fmt.Sprintf("SELECT status, expires FROM authz2 WHERE id IN (%s)", db.QuestionMarks(len(ids))), @@ -1140,11 +1149,11 @@ func getAuthorizationStatuses(s db.Selector, ids []int64) ([]authzValidity, erro return allAuthzValidity, nil } -// authzForOrder retrieves the authorization IDs for an order. It assumes that -// the provided database selector already has a context associated with it. -func authzForOrder(s db.Selector, orderID int64) ([]int64, error) { +// authzForOrder retrieves the authorization IDs for an order. +func authzForOrder(ctx context.Context, s db.Selector, orderID int64) ([]int64, error) { var v2IDs []int64 _, err := s.Select( + ctx, &v2IDs, "SELECT authzID FROM orderToAuthz2 WHERE orderID = ?", orderID, @@ -1153,11 +1162,11 @@ func authzForOrder(s db.Selector, orderID int64) ([]int64, error) { } // namesForOrder finds all of the requested names associated with an order. The -// names are returned in their reversed form (see `sa.ReverseName`). It assumes -// that the provided database selector already has a context associated with it. -func namesForOrder(s db.Selector, orderID int64) ([]string, error) { +// names are returned in their reversed form (see `sa.ReverseName`). +func namesForOrder(ctx context.Context, s db.Selector, orderID int64) ([]string, error) { var reversedNames []string _, err := s.Select( + ctx, &reversedNames, `SELECT reversedName FROM requestedNames diff --git a/sa/model_test.go b/sa/model_test.go index edac2c902..93ef86b0d 100644 --- a/sa/model_test.go +++ b/sa/model_test.go @@ -1,6 +1,7 @@ package sa import ( + "context" "crypto/rand" "crypto/rsa" "crypto/x509" @@ -278,33 +279,35 @@ func TestPopulateAttemptedFieldsBadJSON(t *testing.T) { } func TestCertificatesTableContainsDuplicateSerials(t *testing.T) { + ctx := context.Background() + sa, fc, cleanUp := initSA(t) defer cleanUp() serialString := core.SerialToString(big.NewInt(1337)) // Insert a certificate with a serial of `1337`. - err := insertCertificate(sa.dbMap, fc, "1337.com", "leet", 1337, 1) + err := insertCertificate(ctx, sa.dbMap, fc, "1337.com", "leet", 1337, 1) test.AssertNotError(t, err, "couldn't insert valid certificate") // This should return the certificate that we just inserted. - certA, err := SelectCertificate(sa.dbMap, serialString) + certA, err := SelectCertificate(ctx, sa.dbMap, serialString) test.AssertNotError(t, err, "received an error for a valid query") // Insert a certificate with a serial of `1337` but for a different // hostname. - err = insertCertificate(sa.dbMap, fc, "1337.net", "leet", 1337, 1) + err = insertCertificate(ctx, sa.dbMap, fc, "1337.net", "leet", 1337, 1) test.AssertNotError(t, err, "couldn't insert valid certificate") // Despite a duplicate being present, this shouldn't error. - certB, err := SelectCertificate(sa.dbMap, serialString) + certB, err := SelectCertificate(ctx, sa.dbMap, serialString) test.AssertNotError(t, err, "received an error for a valid query") // Ensure that `certA` and `certB` are the same. test.AssertByteEquals(t, certA.DER, certB.DER) } -func insertCertificate(dbMap *db.WrappedMap, fc clock.FakeClock, hostname, cn string, serial, regID int64) error { +func insertCertificate(ctx context.Context, dbMap *db.WrappedMap, fc clock.FakeClock, hostname, cn string, serial, regID int64) error { serialBigInt := big.NewInt(serial) serialString := core.SerialToString(serialBigInt) @@ -325,7 +328,7 @@ func insertCertificate(dbMap *db.WrappedMap, fc clock.FakeClock, hostname, cn st Expires: template.NotAfter, DER: certDer, } - err := dbMap.Insert(cert) + err := dbMap.Insert(ctx, cert) if err != nil { return err } @@ -349,12 +352,14 @@ func makeKey() rsa.PrivateKey { } func TestIncidentSerialModel(t *testing.T) { + ctx := context.Background() + testIncidentsDbMap, err := DBMapForTest(vars.DBConnIncidentsFullPerms) test.AssertNotError(t, err, "Couldn't create test dbMap") defer test.ResetIncidentsTestDatabase(t) // Inserting and retrieving a row with only the serial populated should work. - _, err = testIncidentsDbMap.Exec( + _, err = testIncidentsDbMap.ExecContext(ctx, "INSERT INTO incident_foo (serial) VALUES (?)", "1337", ) @@ -362,6 +367,7 @@ func TestIncidentSerialModel(t *testing.T) { var res1 incidentSerialModel err = testIncidentsDbMap.SelectOne( + ctx, &res1, "SELECT * FROM incident_foo WHERE serial = ?", "1337", @@ -374,7 +380,7 @@ func TestIncidentSerialModel(t *testing.T) { test.AssertBoxedNil(t, res1.LastNoticeSent, "lastNoticeSent should be NULL") // Inserting and retrieving a row with all columns populated should work. - _, err = testIncidentsDbMap.Exec( + _, err = testIncidentsDbMap.ExecContext(ctx, "INSERT INTO incident_foo (serial, registrationID, orderID, lastNoticeSent) VALUES (?, ?, ?, ?)", "1338", 1, @@ -385,6 +391,7 @@ func TestIncidentSerialModel(t *testing.T) { var res2 incidentSerialModel err = testIncidentsDbMap.SelectOne( + ctx, &res2, "SELECT * FROM incident_foo WHERE serial = ?", "1338", diff --git a/sa/rate_limits.go b/sa/rate_limits.go index 1cd35bd9c..36cdd0d8e 100644 --- a/sa/rate_limits.go +++ b/sa/rate_limits.go @@ -1,6 +1,7 @@ package sa import ( + "context" "strings" "time" @@ -29,9 +30,8 @@ func baseDomain(name string) string { // addCertificatesPerName adds 1 to the rate limit count for the provided // domains, in a specific time bucket. It must be executed in a transaction, and -// the input timeToTheHour must be a time rounded to an hour. It assumes that -// the given db already has a context associated with it. -func (ssa *SQLStorageAuthority) addCertificatesPerName(db db.SelectExecer, names []string, timeToTheHour time.Time) error { +// the input timeToTheHour must be a time rounded to an hour. +func (ssa *SQLStorageAuthority) addCertificatesPerName(ctx context.Context, db db.SelectExecer, names []string, timeToTheHour time.Time) error { // De-duplicate the base domains. baseDomainsMap := make(map[string]bool) var qmarks []string @@ -45,7 +45,7 @@ func (ssa *SQLStorageAuthority) addCertificatesPerName(db db.SelectExecer, names } } - _, err := db.Exec(`INSERT INTO certificatesPerName (eTLDPlusOne, time, count) VALUES `+ + _, err := db.ExecContext(ctx, `INSERT INTO certificatesPerName (eTLDPlusOne, time, count) VALUES `+ strings.Join(qmarks, ", ")+` ON DUPLICATE KEY UPDATE count=count+1;`, values...) if err != nil { @@ -56,15 +56,15 @@ func (ssa *SQLStorageAuthority) addCertificatesPerName(db db.SelectExecer, names } // countCertificates returns the count of certificates issued for a domain's -// eTLD+1 (aka base domain), during a given time range. It assumes that the -// given db already has a context associated with it. -func (ssa *SQLStorageAuthorityRO) countCertificates(dbMap db.Selector, domain string, timeRange *sapb.Range) (int64, time.Time, error) { +// eTLD+1 (aka base domain), during a given time range. +func (ssa *SQLStorageAuthorityRO) countCertificates(ctx context.Context, dbMap db.Selector, domain string, timeRange *sapb.Range) (int64, time.Time, error) { latest := time.Unix(0, timeRange.Latest) var results []struct { Count int64 Time time.Time } _, err := dbMap.Select( + ctx, &results, `SELECT count, time FROM certificatesPerName WHERE eTLDPlusOne = :baseDomain AND @@ -100,10 +100,9 @@ func (ssa *SQLStorageAuthorityRO) countCertificates(dbMap db.Selector, domain st // addNewOrdersRateLimit adds 1 to the rate limit count for the provided ID, in // a specific time bucket. It must be executed in a transaction, and the input -// timeToTheMinute must be a time rounded to a minute. It assumes that the given -// db already has a context associated with it. -func addNewOrdersRateLimit(dbMap db.SelectExecer, regID int64, timeToTheMinute time.Time) error { - _, err := dbMap.Exec(`INSERT INTO newOrdersRL +// timeToTheMinute must be a time rounded to a minute. +func addNewOrdersRateLimit(ctx context.Context, dbMap db.SelectExecer, regID int64, timeToTheMinute time.Time) error { + _, err := dbMap.ExecContext(ctx, `INSERT INTO newOrdersRL (regID, time, count) VALUES (?, ?, 1) ON DUPLICATE KEY UPDATE count=count+1;`, @@ -117,11 +116,11 @@ func addNewOrdersRateLimit(dbMap db.SelectExecer, regID int64, timeToTheMinute t } // countNewOrders returns the count of orders created in the given time range -// for the given registration ID. It assumes that the given db already has a -// context associated with it. -func countNewOrders(dbMap db.Selector, req *sapb.CountOrdersRequest) (*sapb.Count, error) { +// for the given registration ID. +func countNewOrders(ctx context.Context, dbMap db.Selector, req *sapb.CountOrdersRequest) (*sapb.Count, error) { var counts []int64 _, err := dbMap.Select( + ctx, &counts, `SELECT count FROM newOrdersRL WHERE regID = :regID AND diff --git a/sa/rate_limits_test.go b/sa/rate_limits_test.go index f43e42638..e310d6fb7 100644 --- a/sa/rate_limits_test.go +++ b/sa/rate_limits_test.go @@ -1,6 +1,7 @@ package sa import ( + "context" "fmt" "testing" "time" @@ -10,6 +11,8 @@ import ( ) func TestCertsPerNameRateLimitTable(t *testing.T) { + ctx := context.Background() + sa, _, cleanUp := initSA(t) defer cleanUp() @@ -41,11 +44,11 @@ func TestCertsPerNameRateLimitTable(t *testing.T) { } for _, input := range inputs { - tx, err := sa.dbMap.Begin() + tx, err := sa.dbMap.BeginTx(ctx) if err != nil { t.Fatal(err) } - err = sa.addCertificatesPerName(tx, input.names, input.time) + err = sa.addCertificatesPerName(ctx, tx, input.names, input.time) if err != nil { t.Fatal(err) } @@ -78,7 +81,7 @@ func TestCertsPerNameRateLimitTable(t *testing.T) { Earliest: aprilFirst.Add(-1 * time.Second).UnixNano(), Latest: aprilFirst.Add(aWeek).UnixNano(), } - count, earliest, err := sa.countCertificatesByName(sa.dbMap, tc.domainName, timeRange) + count, earliest, err := sa.countCertificatesByName(ctx, sa.dbMap, tc.domainName, timeRange) if err != nil { t.Fatal(err) } @@ -110,27 +113,27 @@ func TestNewOrdersRateLimitTable(t *testing.T) { } for i := 0; i <= 10; i++ { - tx, err := sa.dbMap.Begin() + tx, err := sa.dbMap.BeginTx(ctx) test.AssertNotError(t, err, "failed to open tx") for j := 0; j < i+1; j++ { - err = addNewOrdersRateLimit(tx, manyCountRegID, start.Add(time.Minute*time.Duration(i))) + err = addNewOrdersRateLimit(ctx, tx, manyCountRegID, start.Add(time.Minute*time.Duration(i))) } test.AssertNotError(t, err, "addNewOrdersRateLimit failed") test.AssertNotError(t, tx.Commit(), "failed to commit tx") } - count, err := countNewOrders(sa.dbMap, req) + count, err := countNewOrders(ctx, sa.dbMap, req) test.AssertNotError(t, err, "countNewOrders failed") test.AssertEquals(t, count.Count, int64(0)) req.AccountID = manyCountRegID - count, err = countNewOrders(sa.dbMap, req) + count, err = countNewOrders(ctx, sa.dbMap, req) test.AssertNotError(t, err, "countNewOrders failed") test.AssertEquals(t, count.Count, int64(65)) req.Range.Earliest = start.Add(time.Minute * 5).UnixNano() req.Range.Latest = start.Add(time.Minute * 10).UnixNano() - count, err = countNewOrders(sa.dbMap, req) + count, err = countNewOrders(ctx, sa.dbMap, req) test.AssertNotError(t, err, "countNewOrders failed") test.AssertEquals(t, count.Count, int64(45)) } diff --git a/sa/sa.go b/sa/sa.go index 3fa3d8fb0..4b8f20ec3 100644 --- a/sa/sa.go +++ b/sa/sa.go @@ -107,7 +107,7 @@ func (ssa *SQLStorageAuthority) NewRegistration(ctx context.Context, req *corepb reg.CreatedAt = ssa.clk.Now() - err = ssa.dbMap.WithContext(ctx).Insert(reg) + err = ssa.dbMap.Insert(ctx, reg) if err != nil { if db.IsDuplicate(err) { // duplicate entry error can only happen when jwk_sha256 collides, indicate @@ -125,7 +125,7 @@ func (ssa *SQLStorageAuthority) UpdateRegistration(ctx context.Context, req *cor return nil, errIncompleteRequest } - curr, err := selectRegistration(ssa.dbMap.WithContext(ctx), "id", req.Id) + curr, err := selectRegistration(ctx, ssa.dbMap, "id", req.Id) if err != nil { if db.IsNoRows(err) { return nil, berrors.NotFoundError("registration with ID '%d' not found", req.Id) @@ -141,7 +141,7 @@ func (ssa *SQLStorageAuthority) UpdateRegistration(ctx context.Context, req *cor // Copy the existing registration model's LockCol to the new updated // registration model's LockCol update.LockCol = curr.LockCol - n, err := ssa.dbMap.WithContext(ctx).Update(update) + n, err := ssa.dbMap.Update(ctx, update) if err != nil { if db.IsDuplicate(err) { // duplicate entry error can only happen when jwk_sha256 collides, indicate @@ -162,7 +162,7 @@ func (ssa *SQLStorageAuthority) AddSerial(ctx context.Context, req *sapb.AddSeri if req.Serial == "" || req.RegID == 0 || req.Created == 0 || req.Expires == 0 { return nil, errIncompleteRequest } - err := ssa.dbMap.WithContext(ctx).Insert(&recordedSerialModel{ + err := ssa.dbMap.Insert(ctx, &recordedSerialModel{ Serial: req.Serial, RegistrationID: req.RegID, Created: time.Unix(0, req.Created), @@ -177,7 +177,7 @@ func (ssa *SQLStorageAuthority) AddSerial(ctx context.Context, req *sapb.AddSeri // SetCertificateStatusReady changes a serial's OCSP status from core.OCSPStatusNotReady to core.OCSPStatusGood. // Called when precertificate issuance succeeds. returns an error if the serial doesn't have status core.OCSPStatusNotReady. func (ssa *SQLStorageAuthority) SetCertificateStatusReady(ctx context.Context, req *sapb.Serial) (*emptypb.Empty, error) { - res, err := ssa.dbMap.WithContext(ctx).Exec( + res, err := ssa.dbMap.ExecContext(ctx, `UPDATE certificateStatus SET status = ? WHERE status = ? AND @@ -222,12 +222,12 @@ func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb Expires: parsed.NotAfter, } - _, overallError := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) { + _, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) { // Select to see if precert exists var row struct { Count int64 } - err := txWithCtx.SelectOne(&row, "SELECT COUNT(*) as count FROM precertificates WHERE serial=?", serialHex) + err := tx.SelectOne(ctx, &row, "SELECT COUNT(*) as count FROM precertificates WHERE serial=?", serialHex) if err != nil { return nil, err } @@ -235,7 +235,7 @@ func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb return nil, berrors.DuplicateError("cannot add a duplicate cert") } - err = txWithCtx.Insert(preCertModel) + err = tx.Insert(ctx, preCertModel) if err != nil { return nil, err } @@ -255,7 +255,7 @@ func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb IsExpired: false, IssuerNameID: req.IssuerNameID, } - err = ssa.dbMap.WithContext(ctx).Insert(cs) + err = ssa.dbMap.Insert(ctx, cs) if err != nil { return nil, err } @@ -267,18 +267,19 @@ func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb // would be a misissuance and miscalculating whether the cert is a renewal or // not for the purpose of rate limiting is the least of our troubles. isRenewal, err := ssa.checkFQDNSetExists( - txWithCtx.SelectOne, + ctx, + tx.SelectOne, parsed.DNSNames) if err != nil { return nil, err } - err = addIssuedNames(txWithCtx, parsed, isRenewal) + err = addIssuedNames(ctx, tx, parsed, isRenewal) if err != nil { return nil, err } - err = addKeyHash(txWithCtx, parsed) + err = addKeyHash(ctx, tx, parsed) if err != nil { return nil, err } @@ -314,12 +315,12 @@ func (ssa *SQLStorageAuthority) AddCertificate(ctx context.Context, req *sapb.Ad Expires: parsedCertificate.NotAfter, } - isRenewalRaw, overallError := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) { + isRenewalRaw, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) { // Select to see if cert exists var row struct { Count int64 } - err := txWithCtx.SelectOne(&row, "SELECT COUNT(*) as count FROM certificates WHERE serial=?", serial) + err := tx.SelectOne(ctx, &row, "SELECT COUNT(*) as count FROM certificates WHERE serial=?", serial) if err != nil { return nil, err } @@ -328,7 +329,7 @@ func (ssa *SQLStorageAuthority) AddCertificate(ctx context.Context, req *sapb.Ad } // Save the final certificate - err = txWithCtx.Insert(cert) + err = tx.Insert(ctx, cert) if err != nil { return nil, err } @@ -340,7 +341,8 @@ func (ssa *SQLStorageAuthority) AddCertificate(ctx context.Context, req *sapb.Ad // would be a misissuance and miscalculating whether the cert is a renewal or // not for the purpose of rate limiting is the least of our troubles. isRenewal, err := ssa.checkFQDNSetExists( - txWithCtx.SelectOne, + ctx, + tx.SelectOne, parsedCertificate.DNSNames) if err != nil { return nil, err @@ -367,12 +369,12 @@ func (ssa *SQLStorageAuthority) AddCertificate(ctx context.Context, req *sapb.Ad // for rate limits. Since the effects of failing these writes is slight // miscalculation of rate limits we choose to not fail the AddCertificate // operation if the rate limit update transaction fails. - _, rlTransactionErr := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) { + _, rlTransactionErr := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) { // Add to the rate limit table, but only for new certificates. Renewals // don't count against the certificatesPerName limit. if !isRenewal { timeToTheHour := parsedCertificate.NotBefore.Round(time.Hour) - err := ssa.addCertificatesPerName(txWithCtx, parsedCertificate.DNSNames, timeToTheHour) + err := ssa.addCertificatesPerName(ctx, tx, parsedCertificate.DNSNames, timeToTheHour) if err != nil { return nil, err } @@ -381,7 +383,8 @@ func (ssa *SQLStorageAuthority) AddCertificate(ctx context.Context, req *sapb.Ad // Update the FQDN sets now that there is a final certificate to ensure rate // limits are calculated correctly. err = addFQDNSet( - txWithCtx, + ctx, + tx, parsedCertificate.DNSNames, core.SerialToString(parsedCertificate.SerialNumber), parsedCertificate.NotBefore, @@ -408,7 +411,7 @@ func (ssa *SQLStorageAuthority) DeactivateRegistration(ctx context.Context, req if req == nil || req.Id == 0 { return nil, errIncompleteRequest } - _, err := ssa.dbMap.WithContext(ctx).Exec( + _, err := ssa.dbMap.ExecContext(ctx, "UPDATE registrations SET status = ? WHERE status = ? AND id = ?", string(core.StatusDeactivated), string(core.StatusValid), @@ -426,7 +429,7 @@ func (ssa *SQLStorageAuthority) DeactivateAuthorization2(ctx context.Context, re return nil, errIncompleteRequest } - _, err := ssa.dbMap.Exec( + _, err := ssa.dbMap.ExecContext(ctx, `UPDATE authz2 SET status = :deactivated WHERE id = :id and status IN (:valid,:pending)`, map[string]interface{}{ "deactivated": statusUint(core.StatusDeactivated), @@ -451,7 +454,7 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb return nil, errIncompleteRequest } - output, err := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) { + output, err := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) { // First, insert all of the new authorizations and record their IDs. newAuthzIDs := make([]int64, 0) if len(req.NewAuthzs) != 0 { @@ -485,7 +488,7 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb return nil, err } } - newAuthzIDs, err = inserter.Insert(txWithCtx) + newAuthzIDs, err = inserter.Insert(ctx, tx) if err != nil { return nil, err } @@ -497,7 +500,7 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb Expires: time.Unix(0, req.NewOrder.Expires), Created: ssa.clk.Now(), } - err := txWithCtx.Insert(order) + err := tx.Insert(ctx, order) if err != nil { return nil, err } @@ -519,7 +522,7 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb return nil, err } } - _, err = inserter.Insert(txWithCtx) + _, err = inserter.Insert(ctx, tx) if err != nil { return nil, err } @@ -535,13 +538,13 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb return nil, err } } - _, err = inserter.Insert(txWithCtx) + _, err = inserter.Insert(ctx, tx) if err != nil { return nil, err } // Fifth, insert the FQDNSet entry for the order. - err = addOrderFQDNSet(txWithCtx, req.NewOrder.Names, order.ID, order.RegistrationID, order.Expires) + err = addOrderFQDNSet(ctx, tx, req.NewOrder.Names, order.ID, order.RegistrationID, order.Expires) if err != nil { return nil, err } @@ -563,7 +566,7 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb // Calculate the order status before returning it. Since it may have reused // all valid authorizations the order may be "born" in a ready status. - status, err := statusForOrder(txWithCtx, res, ssa.clk.Now()) + status, err := statusForOrder(ctx, tx, res, ssa.clk.Now()) if err != nil { return nil, err } @@ -581,7 +584,7 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb } // Increment the order creation count - err = addNewOrdersRateLimit(ssa.dbMap.WithContext(ctx), req.NewOrder.RegistrationID, ssa.clk.Now().Truncate(time.Minute)) + err = addNewOrdersRateLimit(ctx, ssa.dbMap, req.NewOrder.RegistrationID, ssa.clk.Now().Truncate(time.Minute)) if err != nil { return nil, err } @@ -596,8 +599,8 @@ func (ssa *SQLStorageAuthority) SetOrderProcessing(ctx context.Context, req *sap if req.Id == 0 { return nil, errIncompleteRequest } - _, overallError := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) { - result, err := txWithCtx.Exec(` + _, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) { + result, err := tx.ExecContext(ctx, ` UPDATE orders SET beganProcessing = ? WHERE id = ? @@ -627,7 +630,7 @@ func (ssa *SQLStorageAuthority) SetOrderError(ctx context.Context, req *sapb.Set if req.Id == 0 || req.Error == nil { return nil, errIncompleteRequest } - _, overallError := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) { + _, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) { om, err := orderToModel(&corepb.Order{ Id: req.Id, Error: req.Error, @@ -636,7 +639,7 @@ func (ssa *SQLStorageAuthority) SetOrderError(ctx context.Context, req *sapb.Set return nil, err } - result, err := txWithCtx.Exec(` + result, err := tx.ExecContext(ctx, ` UPDATE orders SET error = ? WHERE id = ?`, @@ -667,8 +670,8 @@ func (ssa *SQLStorageAuthority) FinalizeOrder(ctx context.Context, req *sapb.Fin if req.Id == 0 || req.CertificateSerial == "" { return nil, errIncompleteRequest } - _, overallError := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) { - result, err := txWithCtx.Exec(` + _, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) { + result, err := tx.ExecContext(ctx, ` UPDATE orders SET certificateSerial = ? WHERE id = ? AND @@ -686,7 +689,7 @@ func (ssa *SQLStorageAuthority) FinalizeOrder(ctx context.Context, req *sapb.Fin // Delete the orderFQDNSet row for the order now that it has been finalized. // We use this table for order reuse and should not reuse a finalized order. - err = deleteOrderFQDNSet(txWithCtx, req.Id) + err = deleteOrderFQDNSet(ctx, tx, req.Id) if err != nil { return nil, err } @@ -770,7 +773,7 @@ func (ssa *SQLStorageAuthority) FinalizeAuthorization2(ctx context.Context, req "validationError": veJSON, } - res, err := ssa.dbMap.Exec(query, params) + res, err := ssa.dbMap.ExecContext(ctx, query, params) if err != nil { return nil, err } @@ -795,7 +798,7 @@ func (ssa *SQLStorageAuthority) RevokeCertificate(ctx context.Context, req *sapb revokedDate := time.Unix(0, req.Date) - res, err := ssa.dbMap.Exec( + res, err := ssa.dbMap.ExecContext(ctx, `UPDATE certificateStatus SET status = ?, revokedReason = ?, @@ -838,7 +841,7 @@ func (ssa *SQLStorageAuthority) UpdateRevokedCertificate(ctx context.Context, re thisUpdate := time.Unix(0, req.Date) revokedDate := time.Unix(0, req.Backdate) - res, err := ssa.dbMap.Exec( + res, err := ssa.dbMap.ExecContext(ctx, `UPDATE certificateStatus SET revokedReason = ?, ocspLastUpdated = ? @@ -887,7 +890,7 @@ func (ssa *SQLStorageAuthority) AddBlockedKey(ctx context.Context, req *sapb.Add qs += ", ?" vals = append(vals, req.RevokedBy) } - _, err := ssa.dbMap.Exec( + _, err := ssa.dbMap.ExecContext(ctx, fmt.Sprintf("INSERT INTO blockedKeys (%s) VALUES (%s)", cols, qs), vals..., ) @@ -904,7 +907,7 @@ func (ssa *SQLStorageAuthority) AddBlockedKey(ctx context.Context, req *sapb.Add // Health implements the grpc.checker interface. func (ssa *SQLStorageAuthority) Health(ctx context.Context) error { - err := ssa.dbMap.WithContext(ctx).SelectOne(new(int), "SELECT 1") + err := ssa.dbMap.SelectOne(ctx, new(int), "SELECT 1") if err != nil { return err } @@ -939,9 +942,10 @@ func (ssa *SQLStorageAuthority) LeaseCRLShard(ctx context.Context, req *sapb.Lea // leased or are previously-unknown indices are considered older than any other // shard. It returns an error if all shards for the issuer are already leased. func (ssa *SQLStorageAuthority) leaseOldestCRLShard(ctx context.Context, req *sapb.LeaseCRLShardRequest) (*sapb.LeaseCRLShardResponse, error) { - shardIdx, err := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) { + shardIdx, err := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) { var shards []*crlShardModel - _, err := txWithCtx.Select( + _, err := tx.Select( + ctx, &shards, `SELECT id, issuerID, idx, thisUpdate, nextUpdate, leasedUntil FROM crlShards @@ -989,7 +993,7 @@ func (ssa *SQLStorageAuthority) leaseOldestCRLShard(ctx context.Context, req *sa } if needToInsert { - _, err = txWithCtx.Exec( + _, err = tx.ExecContext(ctx, `INSERT INTO crlShards (issuerID, idx, leasedUntil) VALUES (?, ?, ?)`, req.IssuerNameID, @@ -997,7 +1001,7 @@ func (ssa *SQLStorageAuthority) leaseOldestCRLShard(ctx context.Context, req *sa req.Until.AsTime(), ) } else { - _, err = txWithCtx.Exec( + _, err = tx.ExecContext(ctx, `UPDATE crlShards SET leasedUntil = ? WHERE issuerID = ? @@ -1032,8 +1036,8 @@ func (ssa *SQLStorageAuthority) leaseSpecificCRLShard(ctx context.Context, req * return nil, fmt.Errorf("request must identify a single shard index: %d != %d", req.MinShardIdx, req.MaxShardIdx) } - _, err := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) { - res, err := txWithCtx.Exec( + _, err := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) { + res, err := tx.ExecContext(ctx, `UPDATE crlShards SET leasedUntil = ? WHERE issuerID = ? @@ -1084,8 +1088,8 @@ func (ssa *SQLStorageAuthority) UpdateCRLShard(ctx context.Context, req *sapb.Up return nil, errIncompleteRequest } - _, err := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) { - res, err := txWithCtx.Exec( + _, err := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) { + res, err := tx.ExecContext(ctx, `UPDATE crlShards SET thisUpdate = ?, nextUpdate = ? WHERE issuerID = ? diff --git a/sa/sa_test.go b/sa/sa_test.go index 3dd44da4c..92a772c19 100644 --- a/sa/sa_test.go +++ b/sa/sa_test.go @@ -127,7 +127,7 @@ func createPendingAuthorization(t *testing.T, sa *SQLStorageAuthority, domain st Token: token, } - err = sa.dbMap.Insert(&am) + err = sa.dbMap.Insert(context.Background(), &am) test.AssertNotError(t, err, "creating test authorization") return am.ID @@ -238,7 +238,6 @@ func TestSelectRegistration(t *testing.T) { sa, _, cleanUp := initSA(t) defer cleanUp() var ctx = context.Background() - var ssaCtx = sa.dbMap.WithContext(ctx) jwk := goodTestJWK() jwkJSON, _ := jwk.MarshalJSON() sha, err := core.KeyDigestB64(jwk.Key) @@ -253,11 +252,11 @@ func TestSelectRegistration(t *testing.T) { test.AssertNotError(t, err, fmt.Sprintf("couldn't create new registration: %s", err)) test.Assert(t, reg.Id != 0, "ID shouldn't be 0") - _, err = selectRegistration(ssaCtx, "id", reg.Id) + _, err = selectRegistration(ctx, sa.dbMap, "id", reg.Id) test.AssertNotError(t, err, "selecting by id should work") - _, err = selectRegistration(ssaCtx, "jwk_sha256", sha) + _, err = selectRegistration(ctx, sa.dbMap, "jwk_sha256", sha) test.AssertNotError(t, err, "selecting by jwk_sha256 should work") - _, err = selectRegistration(ssaCtx, "initialIP", reg.Id) + _, err = selectRegistration(ctx, sa.dbMap, "initialIP", reg.Id) test.AssertError(t, err, "selecting by any other column should not work") } @@ -305,9 +304,10 @@ func TestReplicationLagRetries(t *testing.T) { // findIssuedName is a small helper test function to directly query the // issuedNames table for a given name to find a serial (or return an err). -func findIssuedName(dbMap db.OneSelector, name string) (string, error) { +func findIssuedName(ctx context.Context, dbMap db.OneSelector, name string) (string, error) { var issuedNamesSerial string err := dbMap.SelectOne( + ctx, &issuedNamesSerial, `SELECT serial FROM issuedNames WHERE reversedName = ? @@ -389,6 +389,7 @@ func TestGetSerialMetadata(t *testing.T) { } func TestAddPrecertificate(t *testing.T) { + ctx := context.Background() sa, clk, cleanUp := initSA(t) defer cleanUp() @@ -416,7 +417,7 @@ func TestAddPrecertificate(t *testing.T) { test.AssertEquals(t, clk.Now().UnixNano(), certStatus.OcspLastUpdated) // It should show up in the issued names table - issuedNamesSerial, err := findIssuedName(sa.dbMap, testCert.DNSNames[0]) + issuedNamesSerial, err := findIssuedName(ctx, sa.dbMap, testCert.DNSNames[0]) test.AssertNotError(t, err, "expected no err querying issuedNames for precert") test.AssertEquals(t, issuedNamesSerial, serial) @@ -511,7 +512,7 @@ func TestAddPrecertificateKeyHash(t *testing.T) { test.AssertNotError(t, err, "failed to add precert") var keyHashes []keyHashModel - _, err = sa.dbMap.Select(&keyHashes, "SELECT * FROM keyHashToSerial") + _, err = sa.dbMap.Select(context.Background(), &keyHashes, "SELECT * FROM keyHashToSerial") test.AssertNotError(t, err, "failed to retrieve rows from keyHashToSerial") test.AssertEquals(t, len(keyHashes), 1) test.AssertEquals(t, keyHashes[0].CertSerial, serial) @@ -669,10 +670,10 @@ func TestCountCertificatesByNames(t *testing.T) { interlocker.Add(len(names)) sa.parallelismPerRPC = len(names) oldCertCountFunc := sa.countCertificatesByName - sa.countCertificatesByName = func(sel db.Selector, domain string, timeRange *sapb.Range) (int64, time.Time, error) { + sa.countCertificatesByName = func(ctx context.Context, sel db.Selector, domain string, timeRange *sapb.Range) (int64, time.Time, error) { interlocker.Done() interlocker.Wait() - return oldCertCountFunc(sel, domain, timeRange) + return oldCertCountFunc(ctx, sel, domain, timeRange) } certDER2, err := os.ReadFile("test-cert2.der") @@ -849,15 +850,16 @@ func TestCountRegistrationsByIPRange(t *testing.T) { } func TestFQDNSets(t *testing.T) { + ctx := context.Background() sa, fc, cleanUp := initSA(t) defer cleanUp() - tx, err := sa.dbMap.Begin() + tx, err := sa.dbMap.BeginTx(ctx) test.AssertNotError(t, err, "Failed to open transaction") names := []string{"a.example.com", "B.example.com"} expires := fc.Now().Add(time.Hour * 2).UTC() issued := fc.Now() - err = addFQDNSet(tx, names, "serial", issued, expires) + err = addFQDNSet(ctx, tx, names, "serial", issued, expires) test.AssertNotError(t, err, "Failed to add name set") test.AssertNotError(t, tx.Commit(), "Failed to commit transaction") @@ -878,9 +880,9 @@ func TestFQDNSets(t *testing.T) { test.AssertEquals(t, count.Count, int64(1)) // add another valid set - tx, err = sa.dbMap.Begin() + tx, err = sa.dbMap.BeginTx(ctx) test.AssertNotError(t, err, "Failed to open transaction") - err = addFQDNSet(tx, names, "anotherSerial", issued, expires) + err = addFQDNSet(ctx, tx, names, "anotherSerial", issued, expires) test.AssertNotError(t, err, "Failed to add name set") test.AssertNotError(t, tx.Commit(), "Failed to commit transaction") @@ -891,9 +893,10 @@ func TestFQDNSets(t *testing.T) { test.AssertEquals(t, count.Count, int64(2)) // add an expired set - tx, err = sa.dbMap.Begin() + tx, err = sa.dbMap.BeginTx(ctx) test.AssertNotError(t, err, "Failed to open transaction") err = addFQDNSet( + ctx, tx, names, "yetAnotherSerial", @@ -913,7 +916,7 @@ func TestFQDNSetTimestampsForWindow(t *testing.T) { sa, fc, cleanUp := initSA(t) defer cleanUp() - tx, err := sa.dbMap.Begin() + tx, err := sa.dbMap.BeginTx(ctx) test.AssertNotError(t, err, "Failed to open transaction") names := []string{"a.example.com", "B.example.com"} @@ -931,7 +934,7 @@ func TestFQDNSetTimestampsForWindow(t *testing.T) { // Add an issuance for names inside the window. expires := fc.Now().Add(time.Hour * 2).UTC() firstIssued := fc.Now() - err = addFQDNSet(tx, names, "serial", firstIssued, expires) + err = addFQDNSet(ctx, tx, names, "serial", firstIssued, expires) test.AssertNotError(t, err, "Failed to add name set") test.AssertNotError(t, tx.Commit(), "Failed to commit transaction") @@ -949,9 +952,9 @@ func TestFQDNSetTimestampsForWindow(t *testing.T) { test.AssertEquals(t, firstIssued, time.Unix(0, resp.Timestamps[len(resp.Timestamps)-1]).UTC()) // Add another issuance for names inside the window. - tx, err = sa.dbMap.Begin() + tx, err = sa.dbMap.BeginTx(ctx) test.AssertNotError(t, err, "Failed to open transaction") - err = addFQDNSet(tx, names, "anotherSerial", firstIssued, expires) + err = addFQDNSet(ctx, tx, names, "anotherSerial", firstIssued, expires) test.AssertNotError(t, err, "Failed to add name set") test.AssertNotError(t, tx.Commit(), "Failed to commit transaction") @@ -963,9 +966,9 @@ func TestFQDNSetTimestampsForWindow(t *testing.T) { test.AssertEquals(t, firstIssued, time.Unix(0, resp.Timestamps[len(resp.Timestamps)-1]).UTC()) // Add another issuance for names but just outside the window. - tx, err = sa.dbMap.Begin() + tx, err = sa.dbMap.BeginTx(ctx) test.AssertNotError(t, err, "Failed to open transaction") - err = addFQDNSet(tx, names, "yetAnotherSerial", firstIssued.Add(-window), expires) + err = addFQDNSet(ctx, tx, names, "yetAnotherSerial", firstIssued.Add(-window), expires) test.AssertNotError(t, err, "Failed to add name set") test.AssertNotError(t, tx.Commit(), "Failed to commit transaction") @@ -985,11 +988,11 @@ func TestFQDNSetsExists(t *testing.T) { test.AssertNotError(t, err, "Failed to check FQDN set existence") test.Assert(t, !exists.Exists, "FQDN set shouldn't exist") - tx, err := sa.dbMap.Begin() + tx, err := sa.dbMap.BeginTx(ctx) test.AssertNotError(t, err, "Failed to open transaction") expires := fc.Now().Add(time.Hour * 2).UTC() issued := fc.Now() - err = addFQDNSet(tx, names, "serial", issued, expires) + err = addFQDNSet(ctx, tx, names, "serial", issued, expires) test.AssertNotError(t, err, "Failed to add name set") test.AssertNotError(t, tx.Commit(), "Failed to commit transaction") @@ -1003,7 +1006,7 @@ type queryRecorder struct { args []interface{} } -func (e *queryRecorder) Query(query string, args ...interface{}) (*sql.Rows, error) { +func (e *queryRecorder) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { e.query = query e.args = args return nil, nil @@ -1090,6 +1093,7 @@ func TestAddIssuedNames(t *testing.T) { t.Run(tc.Name, func(t *testing.T) { var e queryRecorder err := addIssuedNames( + ctx, &e, &x509.Certificate{ DNSNames: tc.IssuedNames, @@ -1262,12 +1266,12 @@ func TestNewOrderAndAuthzs(t *testing.T) { test.AssertDeepEquals(t, order.V2Authorizations, []int64{1, 2, 3, 4}) var authzIDs []int64 - _, err = sa.dbMap.Select(&authzIDs, "SELECT authzID FROM orderToAuthz2 WHERE orderID = ?;", order.Id) + _, err = sa.dbMap.Select(ctx, &authzIDs, "SELECT authzID FROM orderToAuthz2 WHERE orderID = ?;", order.Id) test.AssertNotError(t, err, "Failed to count orderToAuthz entries") test.AssertEquals(t, len(authzIDs), 4) test.AssertDeepEquals(t, authzIDs, []int64{1, 2, 3, 4}) - names, err := namesForOrder(sa.dbReadOnlyMap, order.Id) + names, err := namesForOrder(ctx, sa.dbReadOnlyMap, order.Id) test.AssertNotError(t, err, "namesForOrder errored") test.AssertEquals(t, len(names), 4) test.AssertDeepEquals(t, names, []string{"com.a", "com.b", "com.c", "com.d"}) @@ -1343,7 +1347,7 @@ func TestNewOrderAndAuthzs_NewAuthzExpectedFields(t *testing.T) { test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed") // Safely get the authz for the order we created above. - obj, err := sa.dbReadOnlyMap.Get(authzModel{}, order.V2Authorizations[0]) + obj, err := sa.dbReadOnlyMap.Get(ctx, authzModel{}, order.V2Authorizations[0]) test.AssertNotError(t, err, fmt.Sprintf("authorization %d not found", order.V2Authorizations[0])) // To access the data stored in obj at compile time, we type assert obj @@ -1559,17 +1563,17 @@ func TestGetAuthorizations2(t *testing.T) { // Associate authorizations with an order so that GetAuthorizations2 thinks // they are WFE2 authorizations. - err := sa.dbMap.Insert(&orderToAuthzModel{ + err := sa.dbMap.Insert(ctx, &orderToAuthzModel{ OrderID: 1, AuthzID: authzIDA, }) test.AssertNotError(t, err, "sa.dbMap.Insert failed") - err = sa.dbMap.Insert(&orderToAuthzModel{ + err = sa.dbMap.Insert(ctx, &orderToAuthzModel{ OrderID: 1, AuthzID: authzIDB, }) test.AssertNotError(t, err, "sa.dbMap.Insert failed") - err = sa.dbMap.Insert(&orderToAuthzModel{ + err = sa.dbMap.Insert(ctx, &orderToAuthzModel{ OrderID: 1, AuthzID: authzIDC, }) @@ -2176,9 +2180,9 @@ func TestAddCertificateRenewalBit(t *testing.T) { serial := "thrilla" // Add a FQDN set for the names so that it will be considered a renewal - tx, err := sa.dbMap.Begin() + tx, err := sa.dbMap.BeginTx(ctx) test.AssertNotError(t, err, "Failed to open transaction") - err = addFQDNSet(tx, names, serial, issued, expires) + err = addFQDNSet(ctx, tx, names, serial, issued, expires) test.AssertNotError(t, err, "Failed to add name set") test.AssertNotError(t, tx.Commit(), "Failed to commit transaction") @@ -2201,6 +2205,7 @@ func TestAddCertificateRenewalBit(t *testing.T) { t.Helper() var count int err := sa.dbMap.SelectOne( + ctx, &count, `SELECT COUNT(*) FROM issuedNames WHERE reversedName = ? @@ -2929,7 +2934,7 @@ func TestIncidentsForSerial(t *testing.T) { weekAgo := sa.clk.Now().Add(-time.Hour * 24 * 7) // Add a disabled incident. - err = testSADbMap.Insert(&incidentModel{ + err = testSADbMap.Insert(ctx, &incidentModel{ SerialTable: "incident_foo", URL: "https://example.com/foo-incident", RenewBy: sa.clk.Now().Add(time.Hour * 24 * 7), @@ -2943,7 +2948,7 @@ func TestIncidentsForSerial(t *testing.T) { test.AssertEquals(t, len(result.Incidents), 0) // Add an enabled incident. - err = testSADbMap.Insert(&incidentModel{ + err = testSADbMap.Insert(ctx, &incidentModel{ SerialTable: "incident_bar", URL: "https://example.com/test-incident", RenewBy: sa.clk.Now().Add(time.Hour * 24 * 7), @@ -2959,7 +2964,7 @@ func TestIncidentsForSerial(t *testing.T) { OrderID: &one, LastNoticeSent: &weekAgo, } - _, err = testIncidentsDbMap.Exec( + _, err = testIncidentsDbMap.ExecContext(ctx, fmt.Sprintf("INSERT INTO incident_bar (%s) VALUES ('%s', %d, %d, '%s')", "serial, registrationID, orderID, lastNoticeSent", affectedCertA.Serial, @@ -2983,7 +2988,7 @@ func TestIncidentsForSerial(t *testing.T) { OrderID: &two, LastNoticeSent: &weekAgo, } - _, err = testIncidentsDbMap.Exec( + _, err = testIncidentsDbMap.ExecContext(ctx, fmt.Sprintf("INSERT INTO incident_bar (%s) VALUES ('%s', %d, %d, '%s')", "serial, registrationID, orderID, lastNoticeSent", affectedCertB.Serial, @@ -3089,7 +3094,7 @@ func TestSerialsForIncident(t *testing.T) { } for i := range expectedSerials { randInt := func() int64 { return mrand.Int63() } - _, err := testIncidentsDbMap.Exec( + _, err := testIncidentsDbMap.ExecContext(ctx, fmt.Sprintf("INSERT INTO incident_foo (%s) VALUES ('%s', %d, %d, '%s')", "serial, registrationID, orderID, lastNoticeSent", i, @@ -3268,7 +3273,7 @@ func TestLeaseOldestCRLShard(t *testing.T) { // Create 8 shards: 4 for each of 2 issuers. For each issuer, one shard is // currently leased, three are available, and one of those failed to update. - _, err := sa.dbMap.Exec( + _, err := sa.dbMap.ExecContext(ctx, `INSERT INTO crlShards (issuerID, idx, thisUpdate, nextUpdate, leasedUntil) VALUES (1, 0, ?, ?, ?), (1, 1, ?, ?, ?), @@ -3321,6 +3326,7 @@ func TestLeaseOldestCRLShard(t *testing.T) { test.AssertEquals(t, res.ShardIdx, int64(3)) err = sa.dbMap.SelectOne( + ctx, &untilModel, `SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`, res.IssuerNameID, @@ -3344,6 +3350,7 @@ func TestLeaseOldestCRLShard(t *testing.T) { test.AssertEquals(t, res.ShardIdx, int64(1)) err = sa.dbMap.SelectOne( + ctx, &untilModel, `SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`, res.IssuerNameID, @@ -3369,6 +3376,7 @@ func TestLeaseOldestCRLShard(t *testing.T) { test.Assert(t, res.ShardIdx <= 7, "checking leased index") err = sa.dbMap.SelectOne( + ctx, &untilModel, `SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`, res.IssuerNameID, @@ -3388,7 +3396,7 @@ func TestLeaseSpecificCRLShard(t *testing.T) { // Create 8 shards: 4 for each of 2 issuers. For each issuer, one shard is // currently leased, three are available, and one of those failed to update. - _, err := sa.dbMap.Exec( + _, err := sa.dbMap.ExecContext(ctx, `INSERT INTO crlShards (issuerID, idx, thisUpdate, nextUpdate, leasedUntil) VALUES (1, 0, ?, ?, ?), (1, 1, ?, ?, ?), @@ -3429,6 +3437,7 @@ func TestLeaseSpecificCRLShard(t *testing.T) { test.AssertEquals(t, res.ShardIdx, int64(1)) err = sa.dbMap.SelectOne( + ctx, &untilModel, `SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`, res.IssuerNameID, @@ -3452,6 +3461,7 @@ func TestLeaseSpecificCRLShard(t *testing.T) { test.AssertEquals(t, res.ShardIdx, int64(3)) err = sa.dbMap.SelectOne( + ctx, &untilModel, `SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`, res.IssuerNameID, @@ -3496,7 +3506,7 @@ func TestUpdateCRLShard(t *testing.T) { // Create 8 shards: 4 for each of 2 issuers. For each issuer, one shard is // currently leased, three are available, and one of those failed to update. - _, err := sa.dbMap.Exec( + _, err := sa.dbMap.ExecContext(ctx, `INSERT INTO crlShards (issuerID, idx, thisUpdate, nextUpdate, leasedUntil) VALUES (1, 0, ?, ?, ?), (1, 1, ?, ?, ?), @@ -3535,6 +3545,7 @@ func TestUpdateCRLShard(t *testing.T) { test.AssertNotError(t, err, "updating leased shard") err = sa.dbMap.SelectOne( + ctx, &thisUpdateModel, `SELECT thisUpdate FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`, 1, @@ -3556,6 +3567,7 @@ func TestUpdateCRLShard(t *testing.T) { test.AssertNotError(t, err, "updating unleased shard") err = sa.dbMap.SelectOne( + ctx, &thisUpdateModel, `SELECT thisUpdate FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`, 1, diff --git a/sa/saro.go b/sa/saro.go index 5a5054e57..436c1b13a 100644 --- a/sa/saro.go +++ b/sa/saro.go @@ -31,7 +31,7 @@ var ( validIncidentTableRegexp = regexp.MustCompile(`^incident_[0-9a-zA-Z_]{1,100}$`) ) -type certCountFunc func(db db.Selector, domain string, timeRange *sapb.Range) (int64, time.Time, error) +type certCountFunc func(ctx context.Context, db db.Selector, domain string, timeRange *sapb.Range) (int64, time.Time, error) // SQLStorageAuthorityRO defines a read-only subset of a Storage Authority type SQLStorageAuthorityRO struct { @@ -70,7 +70,7 @@ type SQLStorageAuthorityRO struct { } // NewSQLStorageAuthorityRO provides persistence using a SQL backend for -// Boulder. It will modify the given gorp.DbMap by adding relevant tables. +// Boulder. It will modify the given borp.DbMap by adding relevant tables. func NewSQLStorageAuthorityRO( dbReadOnlyMap *db.WrappedMap, dbIncidentsMap *db.WrappedMap, @@ -107,13 +107,13 @@ func (ssa *SQLStorageAuthorityRO) GetRegistration(ctx context.Context, req *sapb return nil, errIncompleteRequest } - model, err := selectRegistration(ssa.dbReadOnlyMap.WithContext(ctx), "id", req.Id) + model, err := selectRegistration(ctx, ssa.dbReadOnlyMap, "id", req.Id) if db.IsNoRows(err) && ssa.lagFactor != 0 { // GetRegistration is often called to validate a JWK belonging to a brand // new account whose registrations table row hasn't propagated to the read // replica yet. If we get a NoRows, wait a little bit and retry, once. ssa.clk.Sleep(ssa.lagFactor) - model, err = selectRegistration(ssa.dbReadOnlyMap.WithContext(ctx), "id", req.Id) + model, err = selectRegistration(ctx, ssa.dbReadOnlyMap, "id", req.Id) if err != nil { if db.IsNoRows(err) { ssa.lagFactorCounter.WithLabelValues("GetRegistration", "notfound").Inc() @@ -154,7 +154,7 @@ func (ssa *SQLStorageAuthorityRO) GetRegistrationByKey(ctx context.Context, req if err != nil { return nil, err } - model, err := selectRegistration(ssa.dbReadOnlyMap.WithContext(ctx), "jwk_sha256", sha) + model, err := selectRegistration(ctx, ssa.dbReadOnlyMap, "jwk_sha256", sha) if err != nil { if db.IsNoRows(err) { return nil, berrors.NotFoundError("no registrations with public key sha256 %q", sha) @@ -223,7 +223,8 @@ func (ssa *SQLStorageAuthorityRO) CountRegistrationsByIP(ctx context.Context, re } var count int64 - err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne( + err := ssa.dbReadOnlyMap.SelectOne( + ctx, &count, `SELECT COUNT(*) FROM registrations WHERE @@ -256,7 +257,8 @@ func (ssa *SQLStorageAuthorityRO) CountRegistrationsByIPRange(ctx context.Contex var count int64 beginIP, endIP := ipRange(req.Ip) - err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne( + err := ssa.dbReadOnlyMap.SelectOne( + ctx, &count, `SELECT COUNT(*) FROM registrations WHERE @@ -321,7 +323,7 @@ func (ssa *SQLStorageAuthorityRO) CountCertificatesByNames(ctx context.Context, return default: } - count, earliest, err := ssa.countCertificatesByName(ssa.dbReadOnlyMap.WithContext(ctx), domain, req.Range) + count, earliest, err := ssa.countCertificatesByName(ctx, ssa.dbReadOnlyMap, domain, req.Range) if err != nil { results <- result{err: err} // Skip any further work @@ -386,7 +388,8 @@ func (ssa *SQLStorageAuthorityRO) GetSerialMetadata(ctx context.Context, req *sa } recordedSerial := recordedSerialModel{} - err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne( + err := ssa.dbReadOnlyMap.SelectOne( + ctx, &recordedSerial, "SELECT * FROM serials WHERE serial = ?", req.Serial, @@ -420,7 +423,7 @@ func (ssa *SQLStorageAuthorityRO) GetCertificate(ctx context.Context, req *sapb. return nil, fmt.Errorf("invalid certificate serial %s", req.Serial) } - cert, err := SelectCertificate(ssa.dbReadOnlyMap.WithContext(ctx), req.Serial) + cert, err := SelectCertificate(ctx, ssa.dbReadOnlyMap, req.Serial) if db.IsNoRows(err) { return nil, berrors.NotFoundError("certificate with serial %q not found", req.Serial) } @@ -446,7 +449,7 @@ func (ssa *SQLStorageAuthorityRO) GetCertificateStatus(ctx context.Context, req return nil, err } - certStatus, err := SelectCertificateStatus(ssa.dbReadOnlyMap.WithContext(ctx), req.Serial) + certStatus, err := SelectCertificateStatus(ctx, ssa.dbReadOnlyMap, req.Serial) if db.IsNoRows(err) { return nil, berrors.NotFoundError("certificate status with serial %q not found", req.Serial) } @@ -472,7 +475,7 @@ func (ssa *SQLStorageAuthorityRO) GetRevocationStatus(ctx context.Context, req * return nil, fmt.Errorf("invalid certificate serial %s", req.Serial) } - status, err := SelectRevocationStatus(ssa.dbReadOnlyMap.WithContext(ctx), req.Serial) + status, err := SelectRevocationStatus(ctx, ssa.dbReadOnlyMap, req.Serial) if err != nil { if db.IsNoRows(err) { return nil, berrors.NotFoundError("certificate status with serial %q not found", req.Serial) @@ -492,7 +495,7 @@ func (ssa *SQLStorageAuthorityRO) CountOrders(ctx context.Context, req *sapb.Cou return nil, errIncompleteRequest } - return countNewOrders(ssa.dbReadOnlyMap.WithContext(ctx), req) + return countNewOrders(ctx, ssa.dbReadOnlyMap, req) } func (ssa *SQLStorageAuthority) CountOrders(ctx context.Context, req *sapb.CountOrdersRequest) (*sapb.Count, error) { @@ -507,7 +510,8 @@ func (ssa *SQLStorageAuthorityRO) CountFQDNSets(ctx context.Context, req *sapb.C } var count int64 - err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne( + err := ssa.dbReadOnlyMap.SelectOne( + ctx, &count, `SELECT COUNT(*) FROM fqdnSets WHERE setHash = ? @@ -533,7 +537,8 @@ func (ssa *SQLStorageAuthorityRO) FQDNSetTimestampsForWindow(ctx context.Context Issued time.Time } var rows []row - _, err := ssa.dbReadOnlyMap.WithContext(ctx).Select( + _, err := ssa.dbReadOnlyMap.Select( + ctx, &rows, `SELECT issued FROM fqdnSets WHERE setHash = ? @@ -563,7 +568,7 @@ func (ssa *SQLStorageAuthorityRO) FQDNSetExists(ctx context.Context, req *sapb.F if len(req.Domains) == 0 { return nil, errIncompleteRequest } - exists, err := ssa.checkFQDNSetExists(ssa.dbReadOnlyMap.WithContext(ctx).SelectOne, req.Domains) + exists, err := ssa.checkFQDNSetExists(ctx, ssa.dbReadOnlyMap.SelectOne, req.Domains) if err != nil { return nil, err } @@ -574,16 +579,17 @@ func (ssa *SQLStorageAuthority) FQDNSetExists(ctx context.Context, req *sapb.FQD return ssa.SQLStorageAuthorityRO.FQDNSetExists(ctx, req) } -// oneSelectorFunc is a func type that matches both gorp.Transaction.SelectOne -// and gorp.DbMap.SelectOne. -type oneSelectorFunc func(holder interface{}, query string, args ...interface{}) error +// oneSelectorFunc is a func type that matches both borp.Transaction.SelectOne +// and borp.DbMap.SelectOne. +type oneSelectorFunc func(ctx context.Context, holder interface{}, query string, args ...interface{}) error // checkFQDNSetExists uses the given oneSelectorFunc to check whether an fqdnSet // for the given names exists. -func (ssa *SQLStorageAuthorityRO) checkFQDNSetExists(selector oneSelectorFunc, names []string) (bool, error) { +func (ssa *SQLStorageAuthorityRO) checkFQDNSetExists(ctx context.Context, selector oneSelectorFunc, names []string) (bool, error) { namehash := HashNames(names) var exists bool err := selector( + ctx, &exists, `SELECT EXISTS (SELECT id FROM fqdnSets WHERE setHash = ? LIMIT 1)`, namehash, @@ -608,7 +614,8 @@ func (ssa *SQLStorageAuthorityRO) PreviousCertificateExists(ctx context.Context, // Find the most recently issued certificate containing this domain name. var serial string - err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne( + err := ssa.dbReadOnlyMap.SelectOne( + ctx, &serial, `SELECT serial FROM issuedNames WHERE reversedName = ? @@ -625,7 +632,8 @@ func (ssa *SQLStorageAuthorityRO) PreviousCertificateExists(ctx context.Context, // Check whether that certificate was issued to the specified account. var count int - err = ssa.dbReadOnlyMap.WithContext(ctx).SelectOne( + err = ssa.dbReadOnlyMap.SelectOne( + ctx, &count, `SELECT COUNT(*) FROM certificates WHERE serial = ? @@ -658,8 +666,8 @@ func (ssa *SQLStorageAuthorityRO) GetOrder(ctx context.Context, req *sapb.OrderR return nil, errIncompleteRequest } - txn := func(txWithCtx db.Executor) (interface{}, error) { - omObj, err := txWithCtx.Get(orderModel{}, req.Id) + txn := func(tx db.Executor) (interface{}, error) { + omObj, err := tx.Get(ctx, orderModel{}, req.Id) if err != nil { if db.IsNoRows(err) { return nil, berrors.NotFoundError("no order found for ID %d", req.Id) @@ -680,13 +688,13 @@ func (ssa *SQLStorageAuthorityRO) GetOrder(ctx context.Context, req *sapb.OrderR return nil, berrors.NotFoundError("no order found for ID %d", req.Id) } - v2AuthzIDs, err := authzForOrder(txWithCtx, order.Id) + v2AuthzIDs, err := authzForOrder(ctx, tx, order.Id) if err != nil { return nil, err } order.V2Authorizations = v2AuthzIDs - names, err := namesForOrder(txWithCtx, order.Id) + names, err := namesForOrder(ctx, tx, order.Id) if err != nil { return nil, err } @@ -700,7 +708,7 @@ func (ssa *SQLStorageAuthorityRO) GetOrder(ctx context.Context, req *sapb.OrderR order.Names = reversedNames // Calculate the status for the order - status, err := statusForOrder(txWithCtx, order, ssa.clk.Now()) + status, err := statusForOrder(ctx, tx, order, ssa.clk.Now()) if err != nil { return nil, err } @@ -772,7 +780,7 @@ func (ssa *SQLStorageAuthorityRO) GetOrderForNames(ctx context.Context, req *sap RegistrationID int64 } var err error - err = ssa.dbReadOnlyMap.WithContext(ctx).SelectOne(&result, ` + err = ssa.dbReadOnlyMap.SelectOne(ctx, &result, ` SELECT orderID, registrationID FROM orderFqdnSets WHERE setHash = ? @@ -814,13 +822,13 @@ func (ssa *SQLStorageAuthorityRO) GetAuthorization2(ctx context.Context, req *sa if req.Id == 0 { return nil, errIncompleteRequest } - obj, err := ssa.dbReadOnlyMap.Get(authzModel{}, req.Id) + obj, err := ssa.dbReadOnlyMap.Get(ctx, authzModel{}, req.Id) if db.IsNoRows(err) && ssa.lagFactor != 0 { // GetAuthorization2 is often called shortly after a new order is created, // sometimes before the order's associated authz rows have propagated to the // read replica yet. If we get a NoRows, wait a little bit and retry, once. ssa.clk.Sleep(ssa.lagFactor) - obj, err = ssa.dbReadOnlyMap.Get(authzModel{}, req.Id) + obj, err = ssa.dbReadOnlyMap.Get(ctx, authzModel{}, req.Id) if err != nil { if db.IsNoRows(err) { ssa.lagFactorCounter.WithLabelValues("GetAuthorization2", "notfound").Inc() @@ -890,6 +898,7 @@ func (ssa *SQLStorageAuthorityRO) GetAuthorizations2(ctx context.Context, req *s ) _, err := ssa.dbReadOnlyMap.Select( + ctx, &authzModels, query, params..., @@ -925,7 +934,8 @@ func (ssa *SQLStorageAuthorityRO) GetPendingAuthorization2(ctx context.Context, return nil, errIncompleteRequest } var am authzModel - err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne( + err := ssa.dbReadOnlyMap.SelectOne( + ctx, &am, fmt.Sprintf(`SELECT %s FROM authz2 WHERE registrationID = :regID AND @@ -964,7 +974,7 @@ func (ssa *SQLStorageAuthorityRO) CountPendingAuthorizations2(ctx context.Contex } var count int64 - err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne(&count, + err := ssa.dbReadOnlyMap.SelectOne(ctx, &count, `SELECT COUNT(*) FROM authz2 WHERE registrationID = :regID AND expires > :expires AND @@ -1003,7 +1013,8 @@ func (ssa *SQLStorageAuthorityRO) GetValidOrderAuthorizations2(ctx context.Conte } var ams []authzModel - _, err := ssa.dbReadOnlyMap.WithContext(ctx).Select( + _, err := ssa.dbReadOnlyMap.Select( + ctx, &ams, fmt.Sprintf(`SELECT %s FROM authz2 LEFT JOIN orderToAuthz2 ON authz2.ID = orderToAuthz2.authzID @@ -1050,7 +1061,8 @@ func (ssa *SQLStorageAuthorityRO) CountInvalidAuthorizations2(ctx context.Contex } var count int64 - err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne( + err := ssa.dbReadOnlyMap.SelectOne( + ctx, &count, `SELECT COUNT(*) FROM authz2 WHERE registrationID = :regID AND @@ -1109,6 +1121,7 @@ func (ssa *SQLStorageAuthorityRO) GetValidAuthorizations2(ctx context.Context, r var authzModels []authzModel _, err := ssa.dbReadOnlyMap.Select( + ctx, &authzModels, query, params..., @@ -1144,7 +1157,7 @@ func (ssa *SQLStorageAuthorityRO) KeyBlocked(ctx context.Context, req *sapb.KeyB } var id int64 - err := ssa.dbReadOnlyMap.SelectOne(&id, `SELECT ID FROM blockedKeys WHERE keyHash = ?`, req.KeyHash) + err := ssa.dbReadOnlyMap.SelectOne(ctx, &id, `SELECT ID FROM blockedKeys WHERE keyHash = ?`, req.KeyHash) if err != nil { if db.IsNoRows(err) { return &sapb.Exists{Exists: false}, nil @@ -1167,7 +1180,7 @@ func (ssa *SQLStorageAuthorityRO) IncidentsForSerial(ctx context.Context, req *s } var activeIncidents []incidentModel - _, err := ssa.dbReadOnlyMap.Select(&activeIncidents, `SELECT * FROM incidents WHERE enabled = 1`) + _, err := ssa.dbReadOnlyMap.Select(ctx, &activeIncidents, `SELECT * FROM incidents WHERE enabled = 1`) if err != nil { if db.IsNoRows(err) { return &sapb.Incidents{}, nil @@ -1178,7 +1191,7 @@ func (ssa *SQLStorageAuthorityRO) IncidentsForSerial(ctx context.Context, req *s var incidentsForSerial []*sapb.Incident for _, i := range activeIncidents { var count int - err := ssa.dbIncidentsMap.SelectOne(&count, fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE serial = ?", + err := ssa.dbIncidentsMap.SelectOne(ctx, &count, fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE serial = ?", i.SerialTable), req.Serial) if err != nil { if db.IsNoRows(err) { @@ -1293,7 +1306,7 @@ func (ssa *SQLStorageAuthorityRO) GetRevokedCerts(req *sapb.GetRevokedCertsReque return fmt.Errorf("initializing db map: %w", err) } - rows, err := selector.Query(stream.Context(), clauses, params...) + rows, err := selector.QueryContext(stream.Context(), clauses, params...) if err != nil { return fmt.Errorf("reading db: %w", err) } @@ -1349,7 +1362,8 @@ func (ssa *SQLStorageAuthorityRO) GetMaxExpiration(ctx context.Context, req *emp var model struct { MaxNotAfter time.Time `db:"maxNotAfter"` } - err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne( + err := ssa.dbReadOnlyMap.SelectOne( + ctx, &model, "SELECT MAX(notAfter) AS maxNotAfter FROM certificateStatus", ) @@ -1365,7 +1379,7 @@ func (ssa *SQLStorageAuthority) GetMaxExpiration(ctx context.Context, req *empty // Health implements the grpc.checker interface. func (ssa *SQLStorageAuthorityRO) Health(ctx context.Context) error { - err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne(new(int), "SELECT 1") + err := ssa.dbReadOnlyMap.SelectOne(ctx, new(int), "SELECT 1") if err != nil { return err } diff --git a/sa/type-converter.go b/sa/type-converter.go index 76861b5f6..b746449cd 100644 --- a/sa/type-converter.go +++ b/sa/type-converter.go @@ -5,14 +5,14 @@ import ( "errors" "fmt" - "github.com/go-gorp/gorp/v3" + "github.com/letsencrypt/borp" "gopkg.in/go-jose/go-jose.v2" "github.com/letsencrypt/boulder/core" "github.com/letsencrypt/boulder/identifier" ) -// BoulderTypeConverter is used by Gorp for storing objects in DB. +// BoulderTypeConverter is used by borp for storing objects in DB. type BoulderTypeConverter struct{} // ToDb converts a Boulder object to one suitable for the DB representation. @@ -40,7 +40,7 @@ func (tc BoulderTypeConverter) ToDb(val interface{}) (interface{}, error) { } // FromDb converts a DB representation back into a Boulder object. -func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) { +func (tc BoulderTypeConverter) FromDb(target interface{}) (borp.CustomScanner, bool) { switch target.(type) { case *identifier.ACMEIdentifier, *[]core.Challenge, *[]string, *[][]int: binder := func(holder, target interface{}) error { @@ -58,7 +58,7 @@ func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, b } return nil } - return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true + return borp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true case *jose.JSONWebKey: binder := func(holder, target interface{}) error { s, ok := holder.(*string) @@ -82,7 +82,7 @@ func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, b } return nil } - return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true + return borp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true case *core.AcmeStatus: binder := func(holder, target interface{}) error { s, ok := holder.(*string) @@ -97,7 +97,7 @@ func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, b *st = core.AcmeStatus(*s) return nil } - return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true + return borp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true case *core.OCSPStatus: binder := func(holder, target interface{}) error { s, ok := holder.(*string) @@ -112,8 +112,8 @@ func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, b *st = core.OCSPStatus(*s) return nil } - return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true + return borp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true default: - return gorp.CustomScanner{}, false + return borp.CustomScanner{}, false } } diff --git a/test/db.go b/test/db.go index c75adbe5e..26212133f 100644 --- a/test/db.go +++ b/test/db.go @@ -1,6 +1,7 @@ package test import ( + "context" "database/sql" "fmt" "io" @@ -15,9 +16,9 @@ var ( // rows in all tables in a database plus close the database // connection. It is satisfied by *sql.DB. type CleanUpDB interface { - Begin() (*sql.Tx, error) - Exec(query string, args ...interface{}) (sql.Result, error) - Query(query string, args ...interface{}) (*sql.Rows, error) + BeginTx(context.Context, *sql.TxOptions) (*sql.Tx, error) + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) io.Closer } @@ -27,7 +28,7 @@ type CleanUpDB interface { // table as this is used by sql-migrate (https://github.com/rubenv/sql-migrate) // to track migrations. If it encounters an error it fails the tests. func ResetBoulderTestDatabase(t testing.TB) func() { - return resetTestDatabase(t, "boulder") + return resetTestDatabase(t, context.Background(), "boulder") } // ResetIncidentsTestDatabase returns a cleanup function which deletes all rows @@ -36,20 +37,20 @@ func ResetBoulderTestDatabase(t testing.TB) func() { // (https://github.com/rubenv/sql-migrate) to track migrations. If it encounters // an error it fails the tests. func ResetIncidentsTestDatabase(t testing.TB) func() { - return resetTestDatabase(t, "incidents") + return resetTestDatabase(t, context.Background(), "incidents") } -func resetTestDatabase(t testing.TB, dbPrefix string) func() { +func resetTestDatabase(t testing.TB, ctx context.Context, dbPrefix string) func() { db, err := sql.Open("mysql", fmt.Sprintf("test_setup@tcp(boulder-proxysql:6033)/%s_sa_test", dbPrefix)) if err != nil { t.Fatalf("Couldn't create db: %s", err) } - err = deleteEverythingInAllTables(db) + err = deleteEverythingInAllTables(ctx, db) if err != nil { t.Fatalf("Failed to delete everything: %s", err) } return func() { - err := deleteEverythingInAllTables(db) + err := deleteEverythingInAllTables(ctx, db) if err != nil { t.Fatalf("Failed to truncate tables after the test: %s", err) } @@ -61,8 +62,8 @@ func resetTestDatabase(t testing.TB, dbPrefix string) func() { // available to the CleanUpDB passed in and resets the autoincrement // counters. See allTableNamesInDB for what is meant by "all tables // available". To be used only in test code. -func deleteEverythingInAllTables(db CleanUpDB) error { - ts, err := allTableNamesInDB(db) +func deleteEverythingInAllTables(ctx context.Context, db CleanUpDB) error { + ts, err := allTableNamesInDB(ctx, db) if err != nil { return err } @@ -72,22 +73,22 @@ func deleteEverythingInAllTables(db CleanUpDB) error { // another connection to make the deletion on. Note that // `alter table` statements will silently cause transactions // to commit, so we do them outside of the transaction. - tx, err := db.Begin() + tx, err := db.BeginTx(ctx, nil) if err != nil { return fmt.Errorf("unable to start transaction to delete all rows from table %#v: %s", tn, err) } - _, err = tx.Exec("set FOREIGN_KEY_CHECKS = 0") + _, err = tx.ExecContext(ctx, "set FOREIGN_KEY_CHECKS = 0") if err != nil { return fmt.Errorf("unable to disable FOREIGN_KEY_CHECKS to delete all rows from table %#v: %s", tn, err) } // 1 = 1 here prevents the MariaDB i_am_a_dummy setting from // rejecting the DELETE for not having a WHERE clause. - _, err = tx.Exec("delete from `" + tn + "` where 1 = 1") + _, err = tx.ExecContext(ctx, "delete from `"+tn+"` where 1 = 1") if err != nil { return fmt.Errorf("unable to delete all rows from table %#v: %s", tn, err) } - _, err = tx.Exec("set FOREIGN_KEY_CHECKS = 1") + _, err = tx.ExecContext(ctx, "set FOREIGN_KEY_CHECKS = 1") if err != nil { return fmt.Errorf("unable to re-enable FOREIGN_KEY_CHECKS to delete all rows from table %#v: %s", tn, err) } @@ -96,7 +97,7 @@ func deleteEverythingInAllTables(db CleanUpDB) error { return fmt.Errorf("unable to commit transaction to delete all rows from table %#v: %s", tn, err) } - _, err = db.Exec("alter table `" + tn + "` AUTO_INCREMENT = 1") + _, err = db.ExecContext(ctx, "alter table `"+tn+"` AUTO_INCREMENT = 1") if err != nil { return fmt.Errorf("unable to reset autoincrement on table %#v: %s", tn, err) } @@ -107,8 +108,8 @@ func deleteEverythingInAllTables(db CleanUpDB) error { // allTableNamesInDB returns the names of the tables available to the passed // CleanUpDB. Omits the 'gorp_migrations' table as this is used by sql-migrate // (https://github.com/rubenv/sql-migrate) to track migrations. -func allTableNamesInDB(db CleanUpDB) ([]string, error) { - r, err := db.Query("select table_name from information_schema.tables t where t.table_schema = DATABASE() and t.table_name != 'gorp_migrations';") +func allTableNamesInDB(ctx context.Context, db CleanUpDB) ([]string, error) { + r, err := db.QueryContext(ctx, "select table_name from information_schema.tables t where t.table_schema = DATABASE() and t.table_name != 'gorp_migrations';") if err != nil { return nil, err } diff --git a/test/integration/cert_storage_failed_test.go b/test/integration/cert_storage_failed_test.go index 53720805e..397ff0da9 100644 --- a/test/integration/cert_storage_failed_test.go +++ b/test/integration/cert_storage_failed_test.go @@ -3,6 +3,7 @@ package integration import ( + "context" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" @@ -92,16 +93,18 @@ func TestIssuanceCertStorageFailed(t *testing.T) { t.Parallel() os.Setenv("DIRECTORY", "http://boulder.service.consul:4001/directory") + ctx := context.Background() + // This test is gated on the StoreLintingCertificateInsteadOfPrecertificate // feature flag. if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" { t.Skip("Skipping test because it requires the StoreLintingCertificateInsteadOfPrecertificate feature flag") } - + db, err := sql.Open("mysql", vars.DBConnSAIntegrationFullPerms) test.AssertNotError(t, err, "failed to open db connection") - _, err = db.Exec(`DROP TRIGGER IF EXISTS fail_ready`) + _, err = db.ExecContext(ctx, `DROP TRIGGER IF EXISTS fail_ready`) test.AssertNotError(t, err, "failed to drop trigger") // Make a specific update to certificateStatus fail, for this test but not others. @@ -114,7 +117,7 @@ func TestIssuanceCertStorageFailed(t *testing.T) { // NOTE: CREATE and DROP TRIGGER do not work in prepared statements. Go's // database/sql will automatically try to use a prepared statement if you pass // any arguments to Exec besides the query itself, so don't do that. - _, err = db.Exec(` + _, err = db.ExecContext(ctx, ` CREATE TRIGGER fail_ready BEFORE UPDATE ON certificateStatus FOR EACH ROW BEGIN @@ -130,8 +133,8 @@ func TestIssuanceCertStorageFailed(t *testing.T) { END `) test.AssertNotError(t, err, "failed to create trigger") - - defer db.Exec(`DROP TRIGGER IF EXISTS fail_ready`) + + defer db.ExecContext(ctx, `DROP TRIGGER IF EXISTS fail_ready`) certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) test.AssertNotError(t, err, "creating random cert key") @@ -157,7 +160,7 @@ func TestIssuanceCertStorageFailed(t *testing.T) { test.AssertNotError(t, err, fmt.Sprintf("revoking via admin-revoker: %s", string(output))) _, err = ocsp_helper.Req(cert, - ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Revoked).WithExpectReason(ocsp.Unspecified)) + ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Revoked).WithExpectReason(ocsp.Unspecified)) // ---- Test revocation by key ---- blockMyKeyDomain := "blockmykey.wantserror.com" @@ -186,10 +189,10 @@ func TestIssuanceCertStorageFailed(t *testing.T) { 1, ) test.AssertNotError(t, err, "revoking second certificate") - + for i := 0; i < 300; i++ { _, err = ocsp_helper.Req(successfulCert, - ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Revoked).WithExpectReason(ocsp.KeyCompromise)) + ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Revoked).WithExpectReason(ocsp.KeyCompromise)) if err == nil { break } diff --git a/vendor/github.com/go-gorp/gorp/v3/.travis.yml b/vendor/github.com/go-gorp/gorp/v3/.travis.yml deleted file mode 100644 index 958d260ac..000000000 --- a/vendor/github.com/go-gorp/gorp/v3/.travis.yml +++ /dev/null @@ -1,36 +0,0 @@ -language: go -go: -- "1.15.x" -- "1.16.x" -- tip - -matrix: - allow_failures: - - go: tip - -services: -- mysql -- postgresql -- sqlite3 - -env: - global: - - secure: RriLxF6+2yMl67hdVv8ImXlu0h62mhcpqjaOgYNU+IEbUQ7hx96CKY6gkpYubW3BgApvF5RH6j3+HKvh2kGp0XhDOYOQCODfBSaSipZ5Aa5RKjsEYLtuVIobvJ80awR9hUeql69+WXs0/s72WThG0qTbOUY4pqHWfteeY235hWM= - -install: - - go get -t -d - - go get -t -d -tags integration - -before_script: -- mysql -e "CREATE DATABASE gorptest;" -- mysql -u root -e "GRANT ALL ON gorptest.* TO gorptest@localhost IDENTIFIED BY 'gorptest'" -- psql -c "CREATE DATABASE gorptest;" -U postgres -- psql -c "CREATE USER "gorptest" WITH SUPERUSER PASSWORD 'gorptest';" -U postgres -- go get github.com/lib/pq -- go get github.com/mattn/go-sqlite3 -- go get github.com/ziutek/mymysql/godrv -- go get github.com/go-sql-driver/mysql -- go get golang.org/x/tools/cmd/cover -- go get github.com/mattn/goveralls - -script: ./test_all.sh diff --git a/vendor/github.com/go-gorp/gorp/v3/dialect_oracle.go b/vendor/github.com/go-gorp/gorp/v3/dialect_oracle.go deleted file mode 100644 index 01a99b8a1..000000000 --- a/vendor/github.com/go-gorp/gorp/v3/dialect_oracle.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2012 James Cooper. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package gorp - -import ( - "fmt" - "reflect" - "strings" -) - -// Implementation of Dialect for Oracle databases. -type OracleDialect struct{} - -func (d OracleDialect) QuerySuffix() string { return "" } - -func (d OracleDialect) CreateIndexSuffix() string { return "" } - -func (d OracleDialect) DropIndexSuffix() string { return "" } - -func (d OracleDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string { - switch val.Kind() { - case reflect.Ptr: - return d.ToSqlType(val.Elem(), maxsize, isAutoIncr) - case reflect.Bool: - return "boolean" - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: - if isAutoIncr { - return "serial" - } - return "integer" - case reflect.Int64, reflect.Uint64: - if isAutoIncr { - return "bigserial" - } - return "bigint" - case reflect.Float64: - return "double precision" - case reflect.Float32: - return "real" - case reflect.Slice: - if val.Elem().Kind() == reflect.Uint8 { - return "bytea" - } - } - - switch val.Name() { - case "NullInt64": - return "bigint" - case "NullFloat64": - return "double precision" - case "NullBool": - return "boolean" - case "NullTime", "Time": - return "timestamp with time zone" - } - - if maxsize > 0 { - return fmt.Sprintf("varchar(%d)", maxsize) - } else { - return "text" - } - -} - -// Returns empty string -func (d OracleDialect) AutoIncrStr() string { - return "" -} - -func (d OracleDialect) AutoIncrBindValue() string { - return "NULL" -} - -func (d OracleDialect) AutoIncrInsertSuffix(col *ColumnMap) string { - return "" -} - -// Returns suffix -func (d OracleDialect) CreateTableSuffix() string { - return "" -} - -func (d OracleDialect) TruncateClause() string { - return "truncate" -} - -// Returns "$(i+1)" -func (d OracleDialect) BindVar(i int) string { - return fmt.Sprintf(":%d", i+1) -} - -// After executing the insert uses the ColMap IdQuery to get the generated id -func (d OracleDialect) InsertQueryToTarget(exec SqlExecutor, insertSql, idSql string, target interface{}, params ...interface{}) error { - _, err := exec.Exec(insertSql, params...) - if err != nil { - return err - } - id, err := exec.SelectInt(idSql) - if err != nil { - return err - } - switch target.(type) { - case *int64: - *(target.(*int64)) = id - case *int32: - *(target.(*int32)) = int32(id) - case int: - *(target.(*int)) = int(id) - default: - return fmt.Errorf("Id field can be int, int32 or int64") - } - return nil -} - -func (d OracleDialect) QuoteField(f string) string { - return `"` + strings.ToUpper(f) + `"` -} - -func (d OracleDialect) QuotedTableForQuery(schema string, table string) string { - if strings.TrimSpace(schema) == "" { - return d.QuoteField(table) - } - - return schema + "." + d.QuoteField(table) -} - -func (d OracleDialect) IfSchemaNotExists(command, schema string) string { - return fmt.Sprintf("%s if not exists", command) -} - -func (d OracleDialect) IfTableExists(command, schema, table string) string { - return fmt.Sprintf("%s if exists", command) -} - -func (d OracleDialect) IfTableNotExists(command, schema, table string) string { - return fmt.Sprintf("%s if not exists", command) -} diff --git a/vendor/github.com/go-gorp/gorp/v3/dialect_postgres.go b/vendor/github.com/go-gorp/gorp/v3/dialect_postgres.go deleted file mode 100644 index 7a9c50bf8..000000000 --- a/vendor/github.com/go-gorp/gorp/v3/dialect_postgres.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2012 James Cooper. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package gorp - -import ( - "fmt" - "reflect" - "strings" - "time" -) - -type PostgresDialect struct { - suffix string - LowercaseFields bool -} - -func (d PostgresDialect) QuerySuffix() string { return ";" } - -func (d PostgresDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string { - switch val.Kind() { - case reflect.Ptr: - return d.ToSqlType(val.Elem(), maxsize, isAutoIncr) - case reflect.Bool: - return "boolean" - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: - if isAutoIncr { - return "serial" - } - return "integer" - case reflect.Int64, reflect.Uint64: - if isAutoIncr { - return "bigserial" - } - return "bigint" - case reflect.Float64: - return "double precision" - case reflect.Float32: - return "real" - case reflect.Slice: - if val.Elem().Kind() == reflect.Uint8 { - return "bytea" - } - } - - switch val.Name() { - case "NullInt64": - return "bigint" - case "NullFloat64": - return "double precision" - case "NullBool": - return "boolean" - case "Time", "NullTime": - return "timestamp with time zone" - } - - if maxsize > 0 { - return fmt.Sprintf("varchar(%d)", maxsize) - } else { - return "text" - } - -} - -// Returns empty string -func (d PostgresDialect) AutoIncrStr() string { - return "" -} - -func (d PostgresDialect) AutoIncrBindValue() string { - return "default" -} - -func (d PostgresDialect) AutoIncrInsertSuffix(col *ColumnMap) string { - return " returning " + d.QuoteField(col.ColumnName) -} - -// Returns suffix -func (d PostgresDialect) CreateTableSuffix() string { - return d.suffix -} - -func (d PostgresDialect) CreateIndexSuffix() string { - return "using" -} - -func (d PostgresDialect) DropIndexSuffix() string { - return "" -} - -func (d PostgresDialect) TruncateClause() string { - return "truncate" -} - -func (d PostgresDialect) SleepClause(s time.Duration) string { - return fmt.Sprintf("pg_sleep(%f)", s.Seconds()) -} - -// Returns "$(i+1)" -func (d PostgresDialect) BindVar(i int) string { - return fmt.Sprintf("$%d", i+1) -} - -func (d PostgresDialect) InsertAutoIncrToTarget(exec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error { - rows, err := exec.Query(insertSql, params...) - if err != nil { - return err - } - defer rows.Close() - - if !rows.Next() { - return fmt.Errorf("No serial value returned for insert: %s Encountered error: %s", insertSql, rows.Err()) - } - if err := rows.Scan(target); err != nil { - return err - } - if rows.Next() { - return fmt.Errorf("more than two serial value returned for insert: %s", insertSql) - } - return rows.Err() -} - -func (d PostgresDialect) QuoteField(f string) string { - if d.LowercaseFields { - return `"` + strings.ToLower(f) + `"` - } - return `"` + f + `"` -} - -func (d PostgresDialect) QuotedTableForQuery(schema string, table string) string { - if strings.TrimSpace(schema) == "" { - return d.QuoteField(table) - } - - return schema + "." + d.QuoteField(table) -} - -func (d PostgresDialect) IfSchemaNotExists(command, schema string) string { - return fmt.Sprintf("%s if not exists", command) -} - -func (d PostgresDialect) IfTableExists(command, schema, table string) string { - return fmt.Sprintf("%s if exists", command) -} - -func (d PostgresDialect) IfTableNotExists(command, schema, table string) string { - return fmt.Sprintf("%s if not exists", command) -} diff --git a/vendor/github.com/go-gorp/gorp/v3/dialect_snowflake.go b/vendor/github.com/go-gorp/gorp/v3/dialect_snowflake.go deleted file mode 100644 index 2e2cb8952..000000000 --- a/vendor/github.com/go-gorp/gorp/v3/dialect_snowflake.go +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2012 James Cooper. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package gorp - -import ( - "fmt" - "reflect" - "strings" -) - -type SnowflakeDialect struct { - suffix string - LowercaseFields bool -} - -func (d SnowflakeDialect) QuerySuffix() string { return ";" } - -func (d SnowflakeDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string { - switch val.Kind() { - case reflect.Ptr: - return d.ToSqlType(val.Elem(), maxsize, isAutoIncr) - case reflect.Bool: - return "boolean" - case reflect.Int, - reflect.Int8, - reflect.Int16, - reflect.Int32, - reflect.Uint, - reflect.Uint8, - reflect.Uint16, - reflect.Uint32: - - if isAutoIncr { - return "serial" - } - return "integer" - case reflect.Int64, reflect.Uint64: - if isAutoIncr { - return "bigserial" - } - return "bigint" - case reflect.Float64: - return "double precision" - case reflect.Float32: - return "real" - case reflect.Slice: - if val.Elem().Kind() == reflect.Uint8 { - return "binary" - } - } - - switch val.Name() { - case "NullInt64": - return "bigint" - case "NullFloat64": - return "double precision" - case "NullBool": - return "boolean" - case "Time", "NullTime": - return "timestamp with time zone" - } - - if maxsize > 0 { - return fmt.Sprintf("varchar(%d)", maxsize) - } else { - return "text" - } - -} - -// Returns empty string -func (d SnowflakeDialect) AutoIncrStr() string { - return "" -} - -func (d SnowflakeDialect) AutoIncrBindValue() string { - return "default" -} - -func (d SnowflakeDialect) AutoIncrInsertSuffix(col *ColumnMap) string { - return "" -} - -// Returns suffix -func (d SnowflakeDialect) CreateTableSuffix() string { - return d.suffix -} - -func (d SnowflakeDialect) CreateIndexSuffix() string { - return "" -} - -func (d SnowflakeDialect) DropIndexSuffix() string { - return "" -} - -func (d SnowflakeDialect) TruncateClause() string { - return "truncate" -} - -// Returns "$(i+1)" -func (d SnowflakeDialect) BindVar(i int) string { - return "?" -} - -func (d SnowflakeDialect) InsertAutoIncrToTarget(exec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error { - rows, err := exec.Query(insertSql, params...) - if err != nil { - return err - } - defer rows.Close() - - if !rows.Next() { - return fmt.Errorf("No serial value returned for insert: %s Encountered error: %s", insertSql, rows.Err()) - } - if err := rows.Scan(target); err != nil { - return err - } - if rows.Next() { - return fmt.Errorf("more than two serial value returned for insert: %s", insertSql) - } - return rows.Err() -} - -func (d SnowflakeDialect) QuoteField(f string) string { - if d.LowercaseFields { - return `"` + strings.ToLower(f) + `"` - } - return `"` + f + `"` -} - -func (d SnowflakeDialect) QuotedTableForQuery(schema string, table string) string { - if strings.TrimSpace(schema) == "" { - return d.QuoteField(table) - } - - return schema + "." + d.QuoteField(table) -} - -func (d SnowflakeDialect) IfSchemaNotExists(command, schema string) string { - return fmt.Sprintf("%s if not exists", command) -} - -func (d SnowflakeDialect) IfTableExists(command, schema, table string) string { - return fmt.Sprintf("%s if exists", command) -} - -func (d SnowflakeDialect) IfTableNotExists(command, schema, table string) string { - return fmt.Sprintf("%s if not exists", command) -} diff --git a/vendor/github.com/go-gorp/gorp/v3/dialect_sqlserver.go b/vendor/github.com/go-gorp/gorp/v3/dialect_sqlserver.go deleted file mode 100644 index ec06aed66..000000000 --- a/vendor/github.com/go-gorp/gorp/v3/dialect_sqlserver.go +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2012 James Cooper. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package gorp - -import ( - "fmt" - "reflect" - "strings" -) - -// Implementation of Dialect for Microsoft SQL Server databases. -// Use gorp.SqlServerDialect{"2005"} for legacy datatypes. -// Tested with driver: github.com/denisenkom/go-mssqldb - -type SqlServerDialect struct { - - // If set to "2005" legacy datatypes will be used - Version string -} - -func (d SqlServerDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string { - switch val.Kind() { - case reflect.Ptr: - return d.ToSqlType(val.Elem(), maxsize, isAutoIncr) - case reflect.Bool: - return "bit" - case reflect.Int8: - return "tinyint" - case reflect.Uint8: - return "smallint" - case reflect.Int16: - return "smallint" - case reflect.Uint16: - return "int" - case reflect.Int, reflect.Int32: - return "int" - case reflect.Uint, reflect.Uint32: - return "bigint" - case reflect.Int64: - return "bigint" - case reflect.Uint64: - return "numeric(20,0)" - case reflect.Float32: - return "float(24)" - case reflect.Float64: - return "float(53)" - case reflect.Slice: - if val.Elem().Kind() == reflect.Uint8 { - return "varbinary" - } - } - - switch val.Name() { - case "NullInt64": - return "bigint" - case "NullFloat64": - return "float(53)" - case "NullBool": - return "bit" - case "NullTime", "Time": - if d.Version == "2005" { - return "datetime" - } - return "datetime2" - } - - if maxsize < 1 { - if d.Version == "2005" { - maxsize = 255 - } else { - return fmt.Sprintf("nvarchar(max)") - } - } - return fmt.Sprintf("nvarchar(%d)", maxsize) -} - -// Returns auto_increment -func (d SqlServerDialect) AutoIncrStr() string { - return "identity(0,1)" -} - -// Empty string removes autoincrement columns from the INSERT statements. -func (d SqlServerDialect) AutoIncrBindValue() string { - return "" -} - -func (d SqlServerDialect) AutoIncrInsertSuffix(col *ColumnMap) string { - return "" -} - -func (d SqlServerDialect) CreateTableSuffix() string { return ";" } - -func (d SqlServerDialect) TruncateClause() string { - return "truncate table" -} - -// Returns "?" -func (d SqlServerDialect) BindVar(i int) string { - return "?" -} - -func (d SqlServerDialect) InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) { - return standardInsertAutoIncr(exec, insertSql, params...) -} - -func (d SqlServerDialect) QuoteField(f string) string { - return "[" + strings.Replace(f, "]", "]]", -1) + "]" -} - -func (d SqlServerDialect) QuotedTableForQuery(schema string, table string) string { - if strings.TrimSpace(schema) == "" { - return d.QuoteField(table) - } - return d.QuoteField(schema) + "." + d.QuoteField(table) -} - -func (d SqlServerDialect) QuerySuffix() string { return ";" } - -func (d SqlServerDialect) IfSchemaNotExists(command, schema string) string { - s := fmt.Sprintf("if schema_id(N'%s') is null %s", schema, command) - return s -} - -func (d SqlServerDialect) IfTableExists(command, schema, table string) string { - var schema_clause string - if strings.TrimSpace(schema) != "" { - schema_clause = fmt.Sprintf("%s.", d.QuoteField(schema)) - } - s := fmt.Sprintf("if object_id('%s%s') is not null %s", schema_clause, d.QuoteField(table), command) - return s -} - -func (d SqlServerDialect) IfTableNotExists(command, schema, table string) string { - var schema_clause string - if strings.TrimSpace(schema) != "" { - schema_clause = fmt.Sprintf("%s.", schema) - } - s := fmt.Sprintf("if object_id('%s%s') is null %s", schema_clause, table, command) - return s -} - -func (d SqlServerDialect) CreateIndexSuffix() string { return "" } -func (d SqlServerDialect) DropIndexSuffix() string { return "" } diff --git a/vendor/github.com/go-gorp/gorp/v3/doc.go b/vendor/github.com/go-gorp/gorp/v3/doc.go deleted file mode 100644 index 593f1c3c1..000000000 --- a/vendor/github.com/go-gorp/gorp/v3/doc.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2012 James Cooper. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -// Package gorp provides a simple way to marshal Go structs to and from -// SQL databases. It uses the database/sql package, and should work with any -// compliant database/sql driver. -// -// Source code and project home: -// https://github.com/go-gorp/gorp -package gorp diff --git a/vendor/github.com/go-gorp/gorp/v3/.gitignore b/vendor/github.com/letsencrypt/borp/.gitignore similarity index 100% rename from vendor/github.com/go-gorp/gorp/v3/.gitignore rename to vendor/github.com/letsencrypt/borp/.gitignore diff --git a/vendor/github.com/letsencrypt/borp/CODEOWNERS b/vendor/github.com/letsencrypt/borp/CODEOWNERS new file mode 100644 index 000000000..0c4ed22ba --- /dev/null +++ b/vendor/github.com/letsencrypt/borp/CODEOWNERS @@ -0,0 +1 @@ +* @letsencrypt/boulder-developers diff --git a/vendor/github.com/go-gorp/gorp/v3/CONTRIBUTING.md b/vendor/github.com/letsencrypt/borp/CONTRIBUTING.md similarity index 100% rename from vendor/github.com/go-gorp/gorp/v3/CONTRIBUTING.md rename to vendor/github.com/letsencrypt/borp/CONTRIBUTING.md diff --git a/vendor/github.com/go-gorp/gorp/v3/LICENSE b/vendor/github.com/letsencrypt/borp/LICENSE similarity index 100% rename from vendor/github.com/go-gorp/gorp/v3/LICENSE rename to vendor/github.com/letsencrypt/borp/LICENSE diff --git a/vendor/github.com/go-gorp/gorp/v3/README.md b/vendor/github.com/letsencrypt/borp/README.md similarity index 65% rename from vendor/github.com/go-gorp/gorp/v3/README.md rename to vendor/github.com/letsencrypt/borp/README.md index 983fe4343..f140696f8 100644 --- a/vendor/github.com/go-gorp/gorp/v3/README.md +++ b/vendor/github.com/letsencrypt/borp/README.md @@ -1,107 +1,16 @@ -# Go Relational Persistence +# Boulder's Object Relational Persistence -[![build status](https://github.com/go-gorp/gorp/actions/workflows/go.yml/badge.svg)](https://github.com/go-gorp/gorp/actions) -[![issues](https://img.shields.io/github/issues/go-gorp/gorp.svg)](https://github.com/go-gorp/gorp/issues) -[![Go Reference](https://pkg.go.dev/badge/github.com/go-gorp/gorp/v3.svg)](https://pkg.go.dev/github.com/go-gorp/gorp/v3) +Once upon a time, there was [gorp](https://github.com/go-gorp/gorp/). Gorp was +good, and Let's Encrypt's adopted it for Boulder's database persistence layer. +The maintainers became busy with other projects, and gorp stopped getting +updated. However, it's still very useful to Boulder, and there are a few tweaks +we'd like to make for our own purposes, so we've forked it. -### Update 2016-11-13: Future versions +We maintain this primarily for Boulder's own use, and intend to make some API +breaking changes during 2023. You are welcome to use our fork if you like it, but +we do not expect to spend a lot of time maintaining it for non-Boulder uses. -As many of the maintainers have become busy with other projects, -progress toward the ever-elusive v2 has slowed to the point that we're -only occasionally making progress outside of merging pull requests. -In the interest of continuing to release, I'd like to lean toward a -more maintainable path forward. - -For the moment, I am releasing a v2 tag with the current feature set -from master, as some of those features have been actively used and -relied on by more than one project. Our next goal is to continue -cleaning up the code base with non-breaking changes as much as -possible, but if/when a breaking change is needed, we'll just release -new versions. This allows us to continue development at whatever pace -we're capable of, without delaying the release of features or refusing -PRs. - -## Introduction - -I hesitate to call gorp an ORM. Go doesn't really have objects, at -least not in the classic Smalltalk/Java sense. There goes the "O". -gorp doesn't know anything about the relationships between your -structs (at least not yet). So the "R" is questionable too (but I use -it in the name because, well, it seemed more clever). - -The "M" is alive and well. Given some Go structs and a database, gorp -should remove a fair amount of boilerplate busy-work from your code. - -I hope that gorp saves you time, minimizes the drudgery of getting -data in and out of your database, and helps your code focus on -algorithms, not infrastructure. - -* Bind struct fields to table columns via API or tag -* Support for embedded structs -* Support for transactions -* Forward engineer db schema from structs (great for unit tests) -* Pre/post insert/update/delete hooks -* Automatically generate insert/update/delete statements for a struct -* Automatic binding of auto increment PKs back to struct after insert -* Delete by primary key(s) -* Select by primary key(s) -* Optional trace sql logging -* Bind arbitrary SQL queries to a struct -* Bind slice to SELECT query results without type assertions -* Use positional or named bind parameters in custom SELECT queries -* Optional optimistic locking using a version column (for - update/deletes) - -## Installation - -Use `go get` or your favorite vendoring tool, using whichever import -path you'd like. - -## Versioning - -We use semantic version tags. Feel free to import through `gopkg.in` -(e.g. `gopkg.in/gorp.v2`) to get the latest tag for a major version, -or check out the tag using your favorite vendoring tool. - -Development is not very active right now, but we have plans to -restructure `gorp` as we continue to move toward a more extensible -system. Whenever a breaking change is needed, the major version will -be bumped. - -The `master` branch is where all development is done, and breaking -changes may happen from time to time. That said, if you want to live -on the bleeding edge and are comfortable updating your code when we -make a breaking change, you may use `github.com/go-gorp/gorp` as your -import path. - -Check the version tags to see what's available. We'll make a good -faith effort to add badges for new versions, but we make no -guarantees. - -## Supported Go versions - -This package is guaranteed to be compatible with the latest 2 major -versions of Go. - -Any earlier versions are only supported on a best effort basis and can -be dropped any time. Go has a great compatibility promise. Upgrading -your program to a newer version of Go should never really be a -problem. - -## Migration guide - -#### Pre-v2 to v2 -Automatic mapping of the version column used in optimistic locking has -been removed as it could cause problems if the type was not int. The -version column must now explicitly be set with -`tablemap.SetVersionCol()`. - -## Help/Support - -Use our [`gitter` channel](https://gitter.im/go-gorp/gorp). We used -to use IRC, but with most of us being pulled in many directions, we -often need the email notifications from `gitter` to yell at us to sign -in. +In particular we maintain this to use with MariaDB 10.5+ and InnoDB. ## Quickstart @@ -110,7 +19,7 @@ package main import ( "database/sql" - "gopkg.in/gorp.v1" + "github.com/letsencrypt/borp" _ "github.com/mattn/go-sqlite3" "log" "time" @@ -146,9 +55,6 @@ func main() { // fetch one row - note use of "post_id" instead of "Id" since column is aliased // - // Postgres users should use $1 instead of ? placeholders - // See 'Known Issues' below - // err = dbmap.SelectOne(&p2, "select * from posts where post_id=?", p2.Id) checkErr(err, "SelectOne failed") log.Println("p2 row:", p2) @@ -195,14 +101,14 @@ func newPost(title, body string) Post { } } -func initDb() *gorp.DbMap { +func initDb() *borp.DbMap { // connect to db using standard Go database/sql API // use whatever database/sql driver you wish db, err := sql.Open("sqlite3", "/tmp/post_db.bin") checkErr(err, "sql.Open failed") - // construct a gorp DbMap - dbmap := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}} + // construct a borp DbMap + dbmap := &borp.DbMap{Db: db, Dialect: borp.SqliteDialect{}} // add a table, setting the table name to 'posts' and // specifying that the Id property is an auto incrementing PK @@ -249,7 +155,7 @@ type Person struct { // Example of using tags to alias fields to column names // The 'db' value is the column name // -// A hyphen will cause gorp to skip this field, similar to the +// A hyphen will cause borp to skip this field, similar to the // Go json package. // // This is equivalent to using the ColMap methods: @@ -275,10 +181,10 @@ Then create a mapper, typically you'd do this one time at app startup: // use whatever database/sql driver you wish db, err := sql.Open("mymysql", "tcp:localhost:3306*mydb/myuser/mypassword") -// construct a gorp DbMap -dbmap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}} +// construct a borp DbMap +dbmap := &borp.DbMap{Db: db, Dialect: borp.MySQLDialect{"InnoDB", "UTF8"}} -// register the structs you wish to use with gorp +// register the structs you wish to use with borp // you can also use the shorter dbmap.AddTable() if you // don't want to override the table name // @@ -292,7 +198,7 @@ t3 := dbmap.AddTableWithName(Product{}, "product_test").SetKeys(true, "Id") ### Struct Embedding -gorp supports embedding structs. For example: +borp supports embedding structs. For example: ```go type Names struct { @@ -309,12 +215,12 @@ es := &WithEmbeddedStruct{-1, Names{FirstName: "Alice", LastName: "Smith"}} err := dbmap.Insert(es) ``` -See the `TestWithEmbeddedStruct` function in `gorp_test.go` for a full example. +See the `TestWithEmbeddedStruct` function in `borp_test.go` for a full example. ### Create/Drop Tables ### Automatically create / drop registered tables. This is useful for unit tests -but is entirely optional. You can of course use gorp with tables created manually, +but is entirely optional. You can of course use borp with tables created manually, or with a separate migration tool (like [sql-migrate](https://github.com/rubenv/sql-migrate), [goose](https://bitbucket.org/liamstask/goose) or [migrate](https://github.com/mattes/migrate)). ```go @@ -333,9 +239,9 @@ dbmap.DropTables() Optionally you can pass in a logger to trace all SQL statements. I recommend enabling this initially while you're getting the feel for what -gorp is doing on your behalf. +borp is doing on your behalf. -Gorp defines a `GorpLogger` interface that Go's built in `log.Logger` satisfies. +Borp defines a `GorpLogger` interface that Go's built in `log.Logger` satisfies. However, you can write your own `GorpLogger` implementation, or use a package such as `glog` if you want more control over how statements are logged. @@ -415,7 +321,7 @@ var post Post err := dbmap.SelectOne(&post, "select * from post where id=?", id) ``` -Want to do joins? Just write the SQL and the struct. gorp will bind them: +Want to do joins? Just write the SQL and the struct. Borp will bind them: ```go // Define a type for your join @@ -459,10 +365,10 @@ if reflect.DeepEqual(list[0], expected) { #### SELECT string or int64 -gorp provides a few convenience methods for selecting a single string or int64. +Borp provides a few convenience methods for selecting a single string or int64. ```go -// select single int64 from db (use $1 instead of ? for postgresql) +// select single int64 from db i64, err := dbmap.SelectInt("select count(*) from foo where blah=?", blahVal) // select single string from db: @@ -472,8 +378,9 @@ s, err := dbmap.SelectStr("select name from foo where blah=?", blahVal) #### Named bind parameters -You may use a map or struct to bind parameters by name. This is currently -only supported in SELECT queries. +You may use a map or struct to bind parameters by name. This is supported in +the Exec and Select* functions on DbMap and Transaction. It is not supported by +Prepare, Query, or QueryRow. ```go _, err := dbm.Select(&dest, "select * from Foo where name = :name and age = :age", map[string]interface{}{ @@ -520,27 +427,26 @@ Use hooks to update data before/after saving to the db. Good for timestamps: ```go // implement the PreInsert and PreUpdate hooks -func (i *Invoice) PreInsert(s gorp.SqlExecutor) error { +func (i *Invoice) PreInsert(s borp.SqlExecutor) error { i.Created = time.Now().UnixNano() i.Updated = i.Created return nil } -func (i *Invoice) PreUpdate(s gorp.SqlExecutor) error { +func (i *Invoice) PreUpdate(s borp.SqlExecutor) error { i.Updated = time.Now().UnixNano() return nil } // You can use the SqlExecutor to cascade additional SQL -// Take care to avoid cycles. gorp won't prevent them. +// Take care to avoid cycles. Borp won't prevent them. // // Here's an example of a cascading delete // -func (p *Person) PreDelete(s gorp.SqlExecutor) error { +func (p *Person) PreDelete(s borp.SqlExecutor) error { query := "delete from invoice_test where PersonId=?" - + _, err := s.Exec(query, p.Id) - if err != nil { return err } @@ -560,20 +466,20 @@ Full list of hooks that you can implement: All have the same signature. for example: - func (p *MyStruct) PostUpdate(s gorp.SqlExecutor) error + func (p *MyStruct) PostUpdate(s borp.SqlExecutor) error ### Optimistic Locking #### Note that this behaviour has changed in v2. See [Migration Guide](#migration-guide). -gorp provides a simple optimistic locking feature, similar to Java's +Borp provides a simple optimistic locking feature, similar to Java's JPA, that will raise an error if you try to update/delete a row whose `version` column has a value different than the one in memory. This provides a safe way to do "select then update" style operations without explicit read and write locks. ```go -// Version is an auto-incremented number, managed by gorp +// Version is an auto-incremented number, managed by borp // If this property is present on your struct, update // operations will be constrained // @@ -606,7 +512,7 @@ p1.LName = "Howard" // Raises error because p1.Version == 1, which is out of date count, err := dbmap.Update(p1) -_, ok := err.(gorp.OptimisticLockError) +_, ok := err.(borp.OptimisticLockError) if ok { // should reach this statement @@ -624,10 +530,6 @@ if ok { Indexes are frequently critical for performance. Here is how to add them to your tables. -NB: SqlServer and Oracle need testing and possible adjustment to the -CreateIndexSuffix() and DropIndexSuffix() methods to make AddIndex() -work for them. - In the example below we put an index both on the Id field, and on the AcctId field. @@ -659,7 +561,7 @@ MariaDB [test]> show create table Account; `AcctId` varchar(255) DEFAULT NULL, PRIMARY KEY (`Id`), UNIQUE KEY `AcctIdIndex` (`AcctId`) USING BTREE <<<--- yes! index added. -) ENGINE=InnoDB DEFAULT CHARSET=utf8 +) ENGINE=InnoDB DEFAULT CHARSET=utf8 +---------+--------------------------+ ``` @@ -667,32 +569,21 @@ MariaDB [test]> show create table Account; ## Database Drivers -gorp uses the Go 1 `database/sql` package. A full list of compliant +borp uses the Go 1 `database/sql` package. A full list of compliant drivers is available here: http://code.google.com/p/go-wiki/wiki/SQLDrivers -Sadly, SQL databases differ on various issues. gorp provides a Dialect +Sadly, SQL databases differ on various issues. borp provides a Dialect interface that should be implemented per database vendor. Dialects are provided for: * MySQL -* PostgreSQL * sqlite3 -Each of these three databases pass the test suite. See `gorp_test.go` +Each of these three databases pass the test suite. See `borp_test.go` for example DSNs for these three databases. -Support is also provided for: - -* Oracle (contributed by @klaidliadon) -* SQL Server (contributed by @qrawl) - use driver: - github.com/denisenkom/go-mssqldb - -Note that these databases are not covered by CI and I (@coopernurse) -have no good way to test them locally. So please try them and send -patches as needed, but expect a bit more unpredicability. - ## Sqlite3 Extensions In order to use sqlite3 extensions you need to first register a custom driver: @@ -721,43 +612,9 @@ func customDriver() (*sql.DB, error) { ## Known Issues -### SQL placeholder portability - -Different databases use different strings to indicate variable -placeholders in prepared SQL statements. Unlike some database -abstraction layers (such as JDBC), Go's `database/sql` does not -standardize this. - -SQL generated by gorp in the `Insert`, `Update`, `Delete`, and `Get` -methods delegates to a Dialect implementation for each database, and -will generate portable SQL. - -Raw SQL strings passed to `Exec`, `Select`, `SelectOne`, `SelectInt`, -etc will not be parsed. Consequently you may have portability issues -if you write a query like this: - -```go -// works on MySQL and Sqlite3, but not with Postgresql err := -dbmap.SelectOne(&val, "select * from foo where id = ?", 30) -``` - -In `Select` and `SelectOne` you can use named parameters to work -around this. The following is portable: - -```go -err := dbmap.SelectOne(&val, "select * from foo where id = :id", -map[string]interface{} { "id": 30}) -``` - -Additionally, when using Postgres as your database, you should utilize -`$1` instead of `?` placeholders as utilizing `?` placeholders when -querying Postgres will result in `pq: operator does not exist` -errors. Alternatively, use `dbMap.Dialect.BindVar(varIdx)` to get the -proper variable binding for your dialect. - ### time.Time and time zones -gorp will pass `time.Time` fields through to the `database/sql` +borp will pass `time.Time` fields through to the `database/sql` driver, but note that the behavior of this type varies across database drivers. @@ -773,7 +630,7 @@ To avoid any potential issues with timezone/DST, consider: ## Running the tests -The included tests may be run against MySQL, Postgresql, or sqlite3. +The included tests may be run against MySQL or sqlite3. You must set two environment variables so the test code knows which driver to use, and how to connect to your database. @@ -790,17 +647,10 @@ go test -bench="Bench" -benchtime 10 ``` Valid `GORP_TEST_DIALECT` values are: "mysql"(for mymysql), -"gomysql"(for go-sql-driver), "postgres", "sqlite" See the +"gomysql"(for go-sql-driver), or "sqlite" See the `test_all.sh` script for examples of all 3 databases. This is the script I run locally to test the library. -## Performance - -gorp uses reflection to construct SQL queries and bind parameters. -See the BenchmarkNativeCrud vs BenchmarkGorpCrud in gorp_test.go for a -simple perf test. On my MacBook Pro gorp is about 2-3% slower than -hand written SQL. - ## Contributors diff --git a/vendor/github.com/go-gorp/gorp/v3/column.go b/vendor/github.com/letsencrypt/borp/column.go similarity index 99% rename from vendor/github.com/go-gorp/gorp/v3/column.go rename to vendor/github.com/letsencrypt/borp/column.go index 383e9efb6..c19782ab3 100644 --- a/vendor/github.com/go-gorp/gorp/v3/column.go +++ b/vendor/github.com/letsencrypt/borp/column.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gorp +package borp import "reflect" diff --git a/vendor/github.com/go-gorp/gorp/v3/db.go b/vendor/github.com/letsencrypt/borp/db.go similarity index 78% rename from vendor/github.com/go-gorp/gorp/v3/db.go rename to vendor/github.com/letsencrypt/borp/db.go index d78062e5b..539941464 100644 --- a/vendor/github.com/go-gorp/gorp/v3/db.go +++ b/vendor/github.com/letsencrypt/borp/db.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gorp +package borp import ( "bytes" @@ -24,12 +24,9 @@ import ( // // Example: // -// dialect := gorp.MySQLDialect{"InnoDB", "UTF8"} -// dbmap := &gorp.DbMap{Db: db, Dialect: dialect} -// +// dialect := borp.MySQLDialect{"InnoDB", "UTF8"} +// dbmap := &borp.DbMap{Db: db, Dialect: dialect} type DbMap struct { - ctx context.Context - // Db handle to use with this map Db *sql.DB @@ -40,53 +37,7 @@ type DbMap struct { // ExpandSlices when enabled will convert slice arguments in mappers into flat // values. It will modify the query, adding more placeholders, and the mapper, - // adding each item of the slice as a new unique entry in the mapper. For - // example, given the scenario bellow: - // - // dbmap.Select(&output, "SELECT 1 FROM example WHERE id IN (:IDs)", map[string]interface{}{ - // "IDs": []int64{1, 2, 3}, - // }) - // - // The executed query would be: - // - // SELECT 1 FROM example WHERE id IN (:IDs0,:IDs1,:IDs2) - // - // With the mapper: - // - // map[string]interface{}{ - // "IDs": []int64{1, 2, 3}, - // "IDs0": int64(1), - // "IDs1": int64(2), - // "IDs2": int64(3), - // } - // - // It is also flexible for custom slice types. The value just need to - // implement stringer or numberer interfaces. - // - // type CustomValue string - // - // const ( - // CustomValueHey CustomValue = "hey" - // CustomValueOh CustomValue = "oh" - // ) - // - // type CustomValues []CustomValue - // - // func (c CustomValues) ToStringSlice() []string { - // values := make([]string, len(c)) - // for i := range c { - // values[i] = string(c[i]) - // } - // return values - // } - // - // func query() { - // // ... - // result, err := dbmap.Select(&output, "SELECT 1 FROM example WHERE value IN (:Values)", map[string]interface{}{ - // "Values": CustomValues([]CustomValue{CustomValueHey}), - // }) - // // ... - // } + // adding each item of the slice as a new unique entry in the mapper. ExpandSliceArgs bool tables []*TableMap @@ -117,19 +68,12 @@ func (m *DbMap) dynamicTableMap() map[string]*TableMap { return m.tablesDynamic } -func (m *DbMap) WithContext(ctx context.Context) SqlExecutor { - copy := &DbMap{} - *copy = *m - copy.ctx = ctx - return copy -} - -func (m *DbMap) CreateIndex() error { +func (m *DbMap) CreateIndex(ctx context.Context) error { var err error dialect := reflect.TypeOf(m.Dialect) for _, table := range m.tables { for _, index := range table.indexes { - err = m.createIndexImpl(dialect, table, index) + err = m.createIndexImpl(ctx, dialect, table, index) if err != nil { break } @@ -138,7 +82,7 @@ func (m *DbMap) CreateIndex() error { for _, table := range m.dynamicTableMap() { for _, index := range table.indexes { - err = m.createIndexImpl(dialect, table, index) + err = m.createIndexImpl(ctx, dialect, table, index) if err != nil { break } @@ -148,7 +92,7 @@ func (m *DbMap) CreateIndex() error { return err } -func (m *DbMap) createIndexImpl(dialect reflect.Type, +func (m *DbMap) createIndexImpl(ctx context.Context, dialect reflect.Type, table *TableMap, index *IndexMap) error { s := bytes.Buffer{} @@ -158,9 +102,6 @@ func (m *DbMap) createIndexImpl(dialect reflect.Type, } s.WriteString(" index") s.WriteString(fmt.Sprintf(" %s on %s", index.IndexName, table.TableName)) - if dname := dialect.Name(); dname == "PostgresDialect" && index.IndexType != "" { - s.WriteString(fmt.Sprintf(" %s %s", m.Dialect.CreateIndexSuffix(), index.IndexType)) - } s.WriteString(" (") for x, col := range index.columns { if x > 0 { @@ -174,11 +115,11 @@ func (m *DbMap) createIndexImpl(dialect reflect.Type, s.WriteString(fmt.Sprintf(" %s %s", m.Dialect.CreateIndexSuffix(), index.IndexType)) } s.WriteString(";") - _, err := m.Exec(s.String()) + _, err := m.ExecContext(ctx, s.String()) return err } -func (t *TableMap) DropIndex(name string) error { +func (t *TableMap) DropIndex(ctx context.Context, name string) error { var err error dialect := reflect.TypeOf(t.dbmap.Dialect) @@ -191,7 +132,7 @@ func (t *TableMap) DropIndex(name string) error { s.WriteString(fmt.Sprintf(" %s %s", t.dbmap.Dialect.DropIndexSuffix(), t.TableName)) } s.WriteString(";") - _, e := t.dbmap.Exec(s.String()) + _, e := t.dbmap.ExecContext(ctx, s.String()) if e != nil { err = e } @@ -202,7 +143,7 @@ func (t *TableMap) DropIndex(name string) error { return err } -// AddTable registers the given interface type with gorp. The table name +// AddTable registers the given interface type with borp. The table name // will be given the name of the TypeOf(i). You must call this function, // or AddTableWithName, for any struct type you wish to persist with // the given DbMap. @@ -248,7 +189,7 @@ func (m *DbMap) AddTableWithNameAndSchema(i interface{}, schema string, name str return tmap } -// AddTableDynamic registers the given interface type with gorp. +// AddTableDynamic registers the given interface type with borp. // The table name will be dynamically determined at runtime by // using the GetTableName method on DynamicTable interface func (m *DbMap) AddTableDynamic(inp DynamicTable, schema string) *TableMap { @@ -418,23 +359,23 @@ func (m *DbMap) readStructColumns(t reflect.Type) (cols []*ColumnMap, primaryKey // // This is particularly useful in unit tests where you want to create // and destroy the schema automatically. -func (m *DbMap) CreateTables() error { - return m.createTables(false) +func (m *DbMap) CreateTables(ctx context.Context) error { + return m.createTables(ctx, false) } // CreateTablesIfNotExists is similar to CreateTables, but starts // each statement with "create table if not exists" so that existing // tables do not raise errors -func (m *DbMap) CreateTablesIfNotExists() error { - return m.createTables(true) +func (m *DbMap) CreateTablesIfNotExists(ctx context.Context) error { + return m.createTables(ctx, true) } -func (m *DbMap) createTables(ifNotExists bool) error { +func (m *DbMap) createTables(ctx context.Context, ifNotExists bool) error { var err error for i := range m.tables { table := m.tables[i] sql := table.SqlForCreate(ifNotExists) - _, err = m.Exec(sql) + _, err = m.ExecContext(ctx, sql) if err != nil { return err } @@ -442,7 +383,7 @@ func (m *DbMap) createTables(ifNotExists bool) error { for _, tbl := range m.dynamicTableMap() { sql := tbl.SqlForCreate(ifNotExists) - _, err = m.Exec(sql) + _, err = m.ExecContext(ctx, sql) if err != nil { return err } @@ -453,7 +394,7 @@ func (m *DbMap) createTables(ifNotExists bool) error { // DropTable drops an individual table. // Returns an error when the table does not exist. -func (m *DbMap) DropTable(table interface{}) error { +func (m *DbMap) DropTable(ctx context.Context, table interface{}) error { t := reflect.TypeOf(table) tableName := "" @@ -461,11 +402,11 @@ func (m *DbMap) DropTable(table interface{}) error { tableName = dyn.TableName() } - return m.dropTable(t, tableName, false) + return m.dropTable(ctx, t, tableName, false) } // DropTableIfExists drops an individual table when the table exists. -func (m *DbMap) DropTableIfExists(table interface{}) error { +func (m *DbMap) DropTableIfExists(ctx context.Context, table interface{}) error { t := reflect.TypeOf(table) tableName := "" @@ -473,34 +414,34 @@ func (m *DbMap) DropTableIfExists(table interface{}) error { tableName = dyn.TableName() } - return m.dropTable(t, tableName, true) + return m.dropTable(ctx, t, tableName, true) } // DropTables iterates through TableMaps registered to this DbMap and // executes "drop table" statements against the database for each. -func (m *DbMap) DropTables() error { - return m.dropTables(false) +func (m *DbMap) DropTables(ctx context.Context) error { + return m.dropTables(ctx, false) } // DropTablesIfExists is the same as DropTables, but uses the "if exists" clause to // avoid errors for tables that do not exist. -func (m *DbMap) DropTablesIfExists() error { - return m.dropTables(true) +func (m *DbMap) DropTablesIfExists(ctx context.Context) error { + return m.dropTables(ctx, true) } // Goes through all the registered tables, dropping them one by one. // If an error is encountered, then it is returned and the rest of // the tables are not dropped. -func (m *DbMap) dropTables(addIfExists bool) (err error) { +func (m *DbMap) dropTables(ctx context.Context, addIfExists bool) (err error) { for _, table := range m.tables { - err = m.dropTableImpl(table, addIfExists) + err = m.dropTableImpl(ctx, table, addIfExists) if err != nil { return err } } for _, table := range m.dynamicTableMap() { - err = m.dropTableImpl(table, addIfExists) + err = m.dropTableImpl(ctx, table, addIfExists) if err != nil { return err } @@ -510,21 +451,21 @@ func (m *DbMap) dropTables(addIfExists bool) (err error) { } // Implementation of dropping a single table. -func (m *DbMap) dropTable(t reflect.Type, name string, addIfExists bool) error { +func (m *DbMap) dropTable(ctx context.Context, t reflect.Type, name string, addIfExists bool) error { table := tableOrNil(m, t, name) if table == nil { return fmt.Errorf("table %s was not registered", table.TableName) } - return m.dropTableImpl(table, addIfExists) + return m.dropTableImpl(ctx, table, addIfExists) } -func (m *DbMap) dropTableImpl(table *TableMap, ifExists bool) (err error) { +func (m *DbMap) dropTableImpl(ctx context.Context, table *TableMap, ifExists bool) (err error) { tableDrop := "drop table" if ifExists { tableDrop = m.Dialect.IfTableExists(tableDrop, table.SchemaName, table.TableName) } - _, err = m.Exec(fmt.Sprintf("%s %s;", tableDrop, m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName))) + _, err = m.ExecContext(ctx, fmt.Sprintf("%s %s;", tableDrop, m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName))) return err } @@ -532,18 +473,18 @@ func (m *DbMap) dropTableImpl(table *TableMap, ifExists bool) (err error) { // executes "truncate table" statements against the database for each, or in the case of // sqlite, a "delete from" with no "where" clause, which uses the truncate optimization // (http://www.sqlite.org/lang_delete.html) -func (m *DbMap) TruncateTables() error { +func (m *DbMap) TruncateTables(ctx context.Context) error { var err error for i := range m.tables { table := m.tables[i] - _, e := m.Exec(fmt.Sprintf("%s %s;", m.Dialect.TruncateClause(), m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName))) + _, e := m.ExecContext(ctx, fmt.Sprintf("%s %s;", m.Dialect.TruncateClause(), m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName))) if e != nil { err = e } } for _, table := range m.dynamicTableMap() { - _, e := m.Exec(fmt.Sprintf("%s %s;", m.Dialect.TruncateClause(), m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName))) + _, e := m.ExecContext(ctx, fmt.Sprintf("%s %s;", m.Dialect.TruncateClause(), m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName))) if e != nil { err = e } @@ -562,8 +503,8 @@ func (m *DbMap) TruncateTables() error { // before/after the INSERT statement if the interface defines them. // // Panics if any interface in the list has not been registered with AddTable -func (m *DbMap) Insert(list ...interface{}) error { - return insert(m, m, list...) +func (m *DbMap) Insert(ctx context.Context, list ...interface{}) error { + return insert(ctx, m, m, list...) } // Update runs a SQL UPDATE statement for each element in list. List @@ -576,8 +517,8 @@ func (m *DbMap) Insert(list ...interface{}) error { // // Returns an error if SetKeys has not been called on the TableMap // Panics if any interface in the list has not been registered with AddTable -func (m *DbMap) Update(list ...interface{}) (int64, error) { - return update(m, m, nil, list...) +func (m *DbMap) Update(ctx context.Context, list ...interface{}) (int64, error) { + return update(ctx, m, m, nil, list...) } // UpdateColumns runs a SQL UPDATE statement for each element in list. List @@ -592,8 +533,8 @@ func (m *DbMap) Update(list ...interface{}) (int64, error) { // // Returns an error if SetKeys has not been called on the TableMap // Panics if any interface in the list has not been registered with AddTable -func (m *DbMap) UpdateColumns(filter ColumnFilter, list ...interface{}) (int64, error) { - return update(m, m, filter, list...) +func (m *DbMap) UpdateColumns(ctx context.Context, filter ColumnFilter, list ...interface{}) (int64, error) { + return update(ctx, m, m, filter, list...) } // Delete runs a SQL DELETE statement for each element in list. List @@ -606,8 +547,8 @@ func (m *DbMap) UpdateColumns(filter ColumnFilter, list ...interface{}) (int64, // // Returns an error if SetKeys has not been called on the TableMap // Panics if any interface in the list has not been registered with AddTable -func (m *DbMap) Delete(list ...interface{}) (int64, error) { - return delete(m, m, list...) +func (m *DbMap) Delete(ctx context.Context, list ...interface{}) (int64, error) { + return delete(ctx, m, m, list...) } // Get runs a SQL SELECT to fetch a single row from the table based on the @@ -625,8 +566,8 @@ func (m *DbMap) Delete(list ...interface{}) (int64, error) { // // Returns an error if SetKeys has not been called on the TableMap // Panics if any interface in the list has not been registered with AddTable -func (m *DbMap) Get(i interface{}, keys ...interface{}) (interface{}, error) { - return get(m, m, i, keys...) +func (m *DbMap) Get(ctx context.Context, i interface{}, keys ...interface{}) (interface{}, error) { + return get(ctx, m, m, i, keys...) } // Select runs an arbitrary SQL query, binding the columns in the result @@ -648,17 +589,17 @@ func (m *DbMap) Get(i interface{}, keys ...interface{}) (interface{}, error) { // and nil returned. // // i does NOT need to be registered with AddTable() -func (m *DbMap) Select(i interface{}, query string, args ...interface{}) ([]interface{}, error) { +func (m *DbMap) Select(ctx context.Context, i interface{}, query string, args ...interface{}) ([]interface{}, error) { if m.ExpandSliceArgs { expandSliceArgs(&query, args...) } - return hookedselect(m, m, i, query, args...) + return hookedselect(ctx, m, m, i, query, args...) } // Exec runs an arbitrary SQL statement. args represent the bind parameters. -// This is equivalent to running: Exec() using database/sql -func (m *DbMap) Exec(query string, args ...interface{}) (sql.Result, error) { +// This is equivalent to running: ExecContext() using database/sql +func (m *DbMap) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { if m.ExpandSliceArgs { expandSliceArgs(&query, args...) } @@ -667,79 +608,84 @@ func (m *DbMap) Exec(query string, args ...interface{}) (sql.Result, error) { now := time.Now() defer m.trace(now, query, args...) } - return maybeExpandNamedQueryAndExec(m, query, args...) + return maybeExpandNamedQueryAndExec(ctx, m, query, args...) } -// SelectInt is a convenience wrapper around the gorp.SelectInt function -func (m *DbMap) SelectInt(query string, args ...interface{}) (int64, error) { +// SelectInt is a convenience wrapper around the borp.SelectInt function +func (m *DbMap) SelectInt(ctx context.Context, query string, args ...interface{}) (int64, error) { if m.ExpandSliceArgs { expandSliceArgs(&query, args...) } - return SelectInt(m, query, args...) + return SelectInt(ctx, m, query, args...) } -// SelectNullInt is a convenience wrapper around the gorp.SelectNullInt function -func (m *DbMap) SelectNullInt(query string, args ...interface{}) (sql.NullInt64, error) { +// SelectNullInt is a convenience wrapper around the borp.SelectNullInt function +func (m *DbMap) SelectNullInt(ctx context.Context, query string, args ...interface{}) (sql.NullInt64, error) { if m.ExpandSliceArgs { expandSliceArgs(&query, args...) } - return SelectNullInt(m, query, args...) + return SelectNullInt(ctx, m, query, args...) } -// SelectFloat is a convenience wrapper around the gorp.SelectFloat function -func (m *DbMap) SelectFloat(query string, args ...interface{}) (float64, error) { +// SelectFloat is a convenience wrapper around the borp.SelectFloat function +func (m *DbMap) SelectFloat(ctx context.Context, query string, args ...interface{}) (float64, error) { if m.ExpandSliceArgs { expandSliceArgs(&query, args...) } - return SelectFloat(m, query, args...) + return SelectFloat(ctx, m, query, args...) } -// SelectNullFloat is a convenience wrapper around the gorp.SelectNullFloat function -func (m *DbMap) SelectNullFloat(query string, args ...interface{}) (sql.NullFloat64, error) { +// SelectNullFloat is a convenience wrapper around the borp.SelectNullFloat function +func (m *DbMap) SelectNullFloat(ctx context.Context, query string, args ...interface{}) (sql.NullFloat64, error) { if m.ExpandSliceArgs { expandSliceArgs(&query, args...) } - return SelectNullFloat(m, query, args...) + return SelectNullFloat(ctx, m, query, args...) } -// SelectStr is a convenience wrapper around the gorp.SelectStr function -func (m *DbMap) SelectStr(query string, args ...interface{}) (string, error) { +// SelectStr is a convenience wrapper around the borp.SelectStr function +func (m *DbMap) SelectStr(ctx context.Context, query string, args ...interface{}) (string, error) { if m.ExpandSliceArgs { expandSliceArgs(&query, args...) } - return SelectStr(m, query, args...) + return SelectStr(ctx, m, query, args...) } -// SelectNullStr is a convenience wrapper around the gorp.SelectNullStr function -func (m *DbMap) SelectNullStr(query string, args ...interface{}) (sql.NullString, error) { +// SelectNullStr is a convenience wrapper around the borp.SelectNullStr function +func (m *DbMap) SelectNullStr(ctx context.Context, query string, args ...interface{}) (sql.NullString, error) { if m.ExpandSliceArgs { expandSliceArgs(&query, args...) } - return SelectNullStr(m, query, args...) + return SelectNullStr(ctx, m, query, args...) } -// SelectOne is a convenience wrapper around the gorp.SelectOne function -func (m *DbMap) SelectOne(holder interface{}, query string, args ...interface{}) error { +// SelectOne is a convenience wrapper around the borp.SelectOne function +func (m *DbMap) SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error { if m.ExpandSliceArgs { expandSliceArgs(&query, args...) } - return SelectOne(m, m, holder, query, args...) + return SelectOne(ctx, m, m, holder, query, args...) } -// Begin starts a gorp Transaction -func (m *DbMap) Begin() (*Transaction, error) { +// BeginTx starts a borp Transaction. It uses database/sql.DB.BeginTx under the hood so the same +// guarantees apply. https://pkg.go.dev/database/sql#DB.BeginTx +// +// > The provided context is used until the transaction is committed or rolled back. If the context +// > is canceled, the sql package will roll back the transaction. Tx.Commit will return an error if +// > the context provided to BeginTx is canceled. +func (m *DbMap) BeginTx(ctx context.Context) (*Transaction, error) { if m.logger != nil { now := time.Now() defer m.trace(now, "begin;") } - tx, err := begin(m) + tx, err := m.Db.BeginTx(ctx, nil) if err != nil { return nil, err } @@ -789,13 +735,13 @@ func (m *DbMap) DynamicTableFor(tableName string, checkPK bool) (*TableMap, erro // Prepare creates a prepared statement for later queries or executions. // Multiple queries or executions may be run concurrently from the returned statement. -// This is equivalent to running: Prepare() using database/sql -func (m *DbMap) Prepare(query string) (*sql.Stmt, error) { +// This is equivalent to running: PrepareContext() using database/sql +func (m *DbMap) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) { if m.logger != nil { now := time.Now() defer m.trace(now, query, nil) } - return prepare(m, query) + return m.Db.PrepareContext(ctx, query) } func tableOrNil(m *DbMap, t reflect.Type, name string) *TableMap { @@ -843,7 +789,8 @@ func (m *DbMap) tableForPointer(ptr interface{}, checkPK bool) (*TableMap, refle return t, elem, nil } -func (m *DbMap) QueryRow(query string, args ...interface{}) *sql.Row { +// This is equivalent to running: QueryRowContext() using database/sql +func (m *DbMap) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row { if m.ExpandSliceArgs { expandSliceArgs(&query, args...) } @@ -852,10 +799,11 @@ func (m *DbMap) QueryRow(query string, args ...interface{}) *sql.Row { now := time.Now() defer m.trace(now, query, args...) } - return queryRow(m, query, args...) + return m.Db.QueryRowContext(ctx, query, args...) } -func (m *DbMap) Query(q string, args ...interface{}) (*sql.Rows, error) { +// This is equivalent to running: QueryContext() using database/sql +func (m *DbMap) QueryContext(ctx context.Context, q string, args ...interface{}) (*sql.Rows, error) { if m.ExpandSliceArgs { expandSliceArgs(&q, args...) } @@ -864,7 +812,7 @@ func (m *DbMap) Query(q string, args ...interface{}) (*sql.Rows, error) { now := time.Now() defer m.trace(now, q, args...) } - return query(m, q, args...) + return m.Db.QueryContext(ctx, q, args...) } func (m *DbMap) trace(started time.Time, query string, args ...interface{}) { @@ -874,7 +822,7 @@ func (m *DbMap) trace(started time.Time, query string, args ...interface{}) { if m.logger != nil { var margs = argsString(args...) - m.logger.Printf("%s%s [%s] (%v)", m.logPrefix, query, margs, (time.Now().Sub(started))) + m.logger.Printf("%s%s [%s] (%v)", m.logPrefix, query, margs, (time.Since(started))) } } diff --git a/vendor/github.com/go-gorp/gorp/v3/dialect.go b/vendor/github.com/letsencrypt/borp/dialect.go similarity index 83% rename from vendor/github.com/go-gorp/gorp/v3/dialect.go rename to vendor/github.com/letsencrypt/borp/dialect.go index fdea2b203..2d48e06d2 100644 --- a/vendor/github.com/go-gorp/gorp/v3/dialect.go +++ b/vendor/github.com/letsencrypt/borp/dialect.go @@ -2,9 +2,10 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gorp +package borp import ( + "context" "reflect" ) @@ -43,8 +44,8 @@ type Dialect interface { // string to truncate tables TruncateClause() string - // bind variable string to use when forming SQL statements - // in many dbs it is "?", but Postgres appears to use $1 + // Bind variable string to use when forming SQL statements + // in many dbs it is "?". // // i is a zero based index of the bind variable in this statement // @@ -72,7 +73,7 @@ type Dialect interface { // the dialect can handle automatic assignment of more than just // integers, see TargetedAutoIncrInserter. type IntegerAutoIncrInserter interface { - InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) + InsertAutoIncr(ctx context.Context, exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) } // TargetedAutoIncrInserter is implemented by dialects that can @@ -83,7 +84,7 @@ type TargetedAutoIncrInserter interface { // automatically generated primary key directly to the passed in // target. The target should be a pointer to the primary key // field of the value being inserted. - InsertAutoIncrToTarget(exec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error + InsertAutoIncrToTarget(ctx context.Context, xec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error } // TargetQueryInserter is implemented by dialects that can perform @@ -93,11 +94,11 @@ type TargetQueryInserter interface { // TargetQueryInserter runs an insert operation and assigns the // automatically generated primary key retrived by the query // extracted from the GeneratedIdQuery field of the id column. - InsertQueryToTarget(exec SqlExecutor, insertSql, idSql string, target interface{}, params ...interface{}) error + InsertQueryToTarget(ctx context.Context, xec SqlExecutor, insertSql, idSql string, target interface{}, params ...interface{}) error } -func standardInsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) { - res, err := exec.Exec(insertSql, params...) +func standardInsertAutoIncr(ctx context.Context, exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) { + res, err := exec.ExecContext(ctx, insertSql, params...) if err != nil { return 0, err } diff --git a/vendor/github.com/go-gorp/gorp/v3/dialect_mysql.go b/vendor/github.com/letsencrypt/borp/dialect_mysql.go similarity index 94% rename from vendor/github.com/go-gorp/gorp/v3/dialect_mysql.go rename to vendor/github.com/letsencrypt/borp/dialect_mysql.go index d068ebe85..1dfc2be68 100644 --- a/vendor/github.com/go-gorp/gorp/v3/dialect_mysql.go +++ b/vendor/github.com/letsencrypt/borp/dialect_mysql.go @@ -2,9 +2,10 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gorp +package borp import ( + "context" "fmt" "reflect" "strings" @@ -140,8 +141,8 @@ func (d MySQLDialect) BindVar(i int) string { return "?" } -func (d MySQLDialect) InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) { - return standardInsertAutoIncr(exec, insertSql, params...) +func (d MySQLDialect) InsertAutoIncr(ctx context.Context, exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) { + return standardInsertAutoIncr(ctx, exec, insertSql, params...) } func (d MySQLDialect) QuoteField(f string) string { diff --git a/vendor/github.com/go-gorp/gorp/v3/dialect_sqlite.go b/vendor/github.com/letsencrypt/borp/dialect_sqlite.go similarity index 91% rename from vendor/github.com/go-gorp/gorp/v3/dialect_sqlite.go rename to vendor/github.com/letsencrypt/borp/dialect_sqlite.go index 2296275b9..a0ba40d66 100644 --- a/vendor/github.com/go-gorp/gorp/v3/dialect_sqlite.go +++ b/vendor/github.com/letsencrypt/borp/dialect_sqlite.go @@ -2,9 +2,10 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gorp +package borp import ( + "context" "fmt" "reflect" ) @@ -86,8 +87,8 @@ func (d SqliteDialect) BindVar(i int) string { return "?" } -func (d SqliteDialect) InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) { - return standardInsertAutoIncr(exec, insertSql, params...) +func (d SqliteDialect) InsertAutoIncr(ctx context.Context, exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) { + return standardInsertAutoIncr(ctx, exec, insertSql, params...) } func (d SqliteDialect) QuoteField(f string) string { diff --git a/vendor/github.com/letsencrypt/borp/doc.go b/vendor/github.com/letsencrypt/borp/doc.go new file mode 100644 index 000000000..15eb824a1 --- /dev/null +++ b/vendor/github.com/letsencrypt/borp/doc.go @@ -0,0 +1,87 @@ +// Copyright 2012 James Cooper. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +// Package borp provides a simple way to marshal Go structs to and from +// SQL databases. It uses the database/sql package, and should work with any +// compliant database/sql driver. +// +// Source code, additional documentation, and examples: +// https://github.com/letsencrypt/borp +// +// # Query Parameters +// +// Borp's Exec, Select*, Query, and QueryRow methods accept placeholder +// parameters in the query, to be filled from the args parameters to these +// functions. Borp supports some additional styles for placeholder parameters: +// +// # Named Bind Parameters +// +// For the Exec and Select* methods on DbMap and Transaction, Borp supports +// named bind parameters. To use named bind parameters, instead of a list of +// parameters, pass a single `map[string]interface{}` to these functions. And +// instead of using ? in the query, use placeholder parameters of the form :word. +// Before running the query, Borp will bind each named placeholder parameter to the +// corresponding value found by looking up "word" in the map. +// +// Example: +// +// _, err := dbm.Select(&dest, "select * from Foo where name = :name and age = :age", +// map[string]interface{}{ +// "name": "Rob", +// "age": 31, +// }) +// +// # Expanding Slices +// +// If you set the ExpandSlices field of DbMap to true, placeholders that bind to +// slices will be handled specially. Borp will modify the query, adding more +// placeholders to match the number of entries in the slice. +// +// For example, given the scenario bellow: +// +// dbmap.Select(&output, "SELECT 1 FROM example WHERE id IN (:IDs)", map[string]interface{}{ +// "IDs": []int64{1, 2, 3}, +// }) +// +// The executed query would be: +// +// SELECT 1 FROM example WHERE id IN (:IDs0,:IDs1,:IDs2) +// +// With the mapper: +// +// map[string]interface{}{ +// "IDs": []int64{1, 2, 3}, +// "IDs0": int64(1), +// "IDs1": int64(2), +// "IDs2": int64(3), +// } +// +// It is also flexible for custom slice types. The value just need to +// implement stringer or numberer interfaces. +// +// type CustomValue string +// +// const ( +// CustomValueHey CustomValue = "hey" +// CustomValueOh CustomValue = "oh" +// ) +// +// type CustomValues []CustomValue +// +// func (c CustomValues) ToStringSlice() []string { +// values := make([]string, len(c)) +// for i := range c { +// values[i] = string(c[i]) +// } +// return values +// } +// +// func query() { +// // ... +// result, err := dbmap.Select(&output, "SELECT 1 FROM example WHERE value IN (:Values)", map[string]interface{}{ +// "Values": CustomValues([]CustomValue{CustomValueHey}), +// }) +// // ... +// } +package borp diff --git a/vendor/github.com/go-gorp/gorp/v3/errors.go b/vendor/github.com/letsencrypt/borp/errors.go similarity index 98% rename from vendor/github.com/go-gorp/gorp/v3/errors.go rename to vendor/github.com/letsencrypt/borp/errors.go index 752ad1bca..65909a126 100644 --- a/vendor/github.com/go-gorp/gorp/v3/errors.go +++ b/vendor/github.com/letsencrypt/borp/errors.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gorp +package borp import ( "fmt" diff --git a/vendor/github.com/go-gorp/gorp/v3/gorp.go b/vendor/github.com/letsencrypt/borp/gorp.go similarity index 75% rename from vendor/github.com/go-gorp/gorp/v3/gorp.go rename to vendor/github.com/letsencrypt/borp/gorp.go index fc6545676..a78caa7d9 100644 --- a/vendor/github.com/go-gorp/gorp/v3/gorp.go +++ b/vendor/github.com/letsencrypt/borp/gorp.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gorp +package borp import ( "context" @@ -15,30 +15,6 @@ import ( "time" ) -// OracleString (empty string is null) -// TODO: move to dialect/oracle?, rename to String? -type OracleString struct { - sql.NullString -} - -// Scan implements the Scanner interface. -func (os *OracleString) Scan(value interface{}) error { - if value == nil { - os.String, os.Valid = "", false - return nil - } - os.Valid = true - return os.NullString.Scan(value) -} - -// Value implements the driver Valuer interface. -func (os OracleString) Value() (driver.Value, error) { - if !os.Valid || os.String == "" { - return nil, nil - } - return os.String, nil -} - // SqlTyper is a type that returns its database type. Most of the // time, the type can just use "database/sql/driver".Valuer; but when // it returns nil for its empty value, it needs to implement SqlTyper @@ -61,7 +37,6 @@ func (nt *dummyField) Scan(value interface{}) error { return nil } -var zeroVal reflect.Value var versFieldConst = "[gorp_ver_field]" // The TypeConverter interface provides a way to map a value of one @@ -90,22 +65,28 @@ type TypeConverter interface { // See the DbMap function docs for each of the functions below for more // information. type SqlExecutor interface { - WithContext(ctx context.Context) SqlExecutor - Get(i interface{}, keys ...interface{}) (interface{}, error) - Insert(list ...interface{}) error - Update(list ...interface{}) (int64, error) - Delete(list ...interface{}) (int64, error) - Exec(query string, args ...interface{}) (sql.Result, error) - Select(i interface{}, query string, args ...interface{}) ([]interface{}, error) - SelectInt(query string, args ...interface{}) (int64, error) - SelectNullInt(query string, args ...interface{}) (sql.NullInt64, error) - SelectFloat(query string, args ...interface{}) (float64, error) - SelectNullFloat(query string, args ...interface{}) (sql.NullFloat64, error) - SelectStr(query string, args ...interface{}) (string, error) - SelectNullStr(query string, args ...interface{}) (sql.NullString, error) - SelectOne(holder interface{}, query string, args ...interface{}) error - Query(query string, args ...interface{}) (*sql.Rows, error) - QueryRow(query string, args ...interface{}) *sql.Row + Get(ctx context.Context, i interface{}, keys ...interface{}) (interface{}, error) + Insert(ctx context.Context, list ...interface{}) error + Update(ctx context.Context, list ...interface{}) (int64, error) + Delete(ctx context.Context, list ...interface{}) (int64, error) + Select(ctx context.Context, i interface{}, query string, args ...interface{}) ([]interface{}, error) + SelectInt(ctx context.Context, query string, args ...interface{}) (int64, error) + SelectNullInt(ctx context.Context, query string, args ...interface{}) (sql.NullInt64, error) + SelectFloat(ctx context.Context, query string, args ...interface{}) (float64, error) + SelectNullFloat(ctx context.Context, query string, args ...interface{}) (sql.NullFloat64, error) + SelectStr(ctx context.Context, query string, args ...interface{}) (string, error) + SelectNullStr(ctx context.Context, query string, args ...interface{}) (sql.NullString, error) + SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error + + // These method signatures are shared with *sql.DB. + // Stylistically, `Context` in the name is redundant. It is the future, everything takes a context. + // But since these three functions delegate to functions of the same name on *sql.DB, it would be + // a little confusing if we had, e.g. `Exec` (taking a context), which delegates to + // `sql.DB.ExecContext` (taking a context) as opposed to `sql.DB.Exec` (taking no context). + // So we don't bother with `Context` in the name for Get, Insert, etc., but we do for these. + ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) + QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) + QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row } // DynamicTable allows the users of gorp to dynamically @@ -156,14 +137,21 @@ func argsString(args ...interface{}) string { // Calls the Exec function on the executor, but attempts to expand any eligible named // query arguments first. -func maybeExpandNamedQueryAndExec(e SqlExecutor, query string, args ...interface{}) (sql.Result, error) { +func maybeExpandNamedQueryAndExec(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (sql.Result, error) { dbMap := extractDbMap(e) if len(args) == 1 { query, args = maybeExpandNamedQuery(dbMap, query, args) } - return exec(e, query, args...) + switch m := e.(type) { + case *DbMap: + return m.Db.ExecContext(ctx, query, args...) + case *Transaction: + return m.tx.ExecContext(ctx, query, args...) + default: + return nil, fmt.Errorf("gorp: unknown SqlExecutor type: %T", m) + } } func extractDbMap(e SqlExecutor) *DbMap { @@ -176,29 +164,6 @@ func extractDbMap(e SqlExecutor) *DbMap { return nil } -// executor exposes the sql.DB and sql.Tx functions so that it can be used -// on internal functions that need to be agnostic to the underlying object. -type executor interface { - Exec(query string, args ...interface{}) (sql.Result, error) - Prepare(query string) (*sql.Stmt, error) - QueryRow(query string, args ...interface{}) *sql.Row - Query(query string, args ...interface{}) (*sql.Rows, error) - ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) - PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) - QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row - QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) -} - -func extractExecutorAndContext(e SqlExecutor) (executor, context.Context) { - switch m := e.(type) { - case *DbMap: - return m.Db, m.ctx - case *Transaction: - return m.tx, m.ctx - } - return nil, nil -} - // maybeExpandNamedQuery checks the given arg to see if it's eligible to be used // as input to a named query. If so, it rewrites the query to use // dialect-dependent bindvars and instantiates the corresponding slice of @@ -311,12 +276,13 @@ func fieldByName(val reflect.Value, fieldName string) *reflect.Value { // try to find field by exact match f := val.FieldByName(fieldName) + var zeroVal reflect.Value if f != zeroVal { return &f } - // try to find by case insensitive match - only the Postgres driver - // seems to require this - in the case where columns are aliased in the sql + // try to find by case insensitive match in the case where columns are + // aliased in the sql fieldNameL := strings.ToLower(fieldName) fieldCount := val.NumField() t := val.Type() @@ -387,7 +353,7 @@ func tableFor(m *DbMap, t reflect.Type, i interface{}) (*foundTable, error) { return &foundTable{table: table}, nil } -func get(m *DbMap, exec SqlExecutor, i interface{}, +func get(ctx context.Context, m *DbMap, exec SqlExecutor, i interface{}, keys ...interface{}) (interface{}, error) { t, err := toType(i) @@ -427,7 +393,7 @@ func get(m *DbMap, exec SqlExecutor, i interface{}, dest[x] = target } - row := exec.QueryRow(plan.query, keys...) + row := exec.QueryRowContext(ctx, plan.query, keys...) err = row.Scan(dest...) if err != nil { if err == sql.ErrNoRows { @@ -453,7 +419,7 @@ func get(m *DbMap, exec SqlExecutor, i interface{}, return v.Interface(), nil } -func delete(m *DbMap, exec SqlExecutor, list ...interface{}) (int64, error) { +func delete(ctx context.Context, m *DbMap, exec SqlExecutor, list ...interface{}) (int64, error) { count := int64(0) for _, ptr := range list { table, elem, err := m.tableForPointer(ptr, true) @@ -474,7 +440,7 @@ func delete(m *DbMap, exec SqlExecutor, list ...interface{}) (int64, error) { return -1, err } - res, err := exec.Exec(bi.query, bi.args...) + res, err := exec.ExecContext(ctx, bi.query, bi.args...) if err != nil { return -1, err } @@ -484,7 +450,7 @@ func delete(m *DbMap, exec SqlExecutor, list ...interface{}) (int64, error) { } if rows == 0 && bi.existingVersion > 0 { - return lockError(m, exec, table.TableName, + return lockError(ctx, m, exec, table.TableName, bi.existingVersion, elem, bi.keys...) } @@ -501,7 +467,7 @@ func delete(m *DbMap, exec SqlExecutor, list ...interface{}) (int64, error) { return count, nil } -func update(m *DbMap, exec SqlExecutor, colFilter ColumnFilter, list ...interface{}) (int64, error) { +func update(ctx context.Context, m *DbMap, exec SqlExecutor, colFilter ColumnFilter, list ...interface{}) (int64, error) { count := int64(0) for _, ptr := range list { table, elem, err := m.tableForPointer(ptr, true) @@ -522,7 +488,7 @@ func update(m *DbMap, exec SqlExecutor, colFilter ColumnFilter, list ...interfac return -1, err } - res, err := exec.Exec(bi.query, bi.args...) + res, err := exec.ExecContext(ctx, bi.query, bi.args...) if err != nil { return -1, err } @@ -533,7 +499,7 @@ func update(m *DbMap, exec SqlExecutor, colFilter ColumnFilter, list ...interfac } if rows == 0 && bi.existingVersion > 0 { - return lockError(m, exec, table.TableName, + return lockError(ctx, m, exec, table.TableName, bi.existingVersion, elem, bi.keys...) } @@ -553,7 +519,7 @@ func update(m *DbMap, exec SqlExecutor, colFilter ColumnFilter, list ...interfac return count, nil } -func insert(m *DbMap, exec SqlExecutor, list ...interface{}) error { +func insert(ctx context.Context, m *DbMap, exec SqlExecutor, list ...interface{}) error { for _, ptr := range list { table, elem, err := m.tableForPointer(ptr, false) if err != nil { @@ -577,7 +543,7 @@ func insert(m *DbMap, exec SqlExecutor, list ...interface{}) error { f := elem.FieldByName(bi.autoIncrFieldName) switch inserter := m.Dialect.(type) { case IntegerAutoIncrInserter: - id, err := inserter.InsertAutoIncr(exec, bi.query, bi.args...) + id, err := inserter.InsertAutoIncr(ctx, exec, bi.query, bi.args...) if err != nil { return err } @@ -590,7 +556,7 @@ func insert(m *DbMap, exec SqlExecutor, list ...interface{}) error { return fmt.Errorf("gorp: cannot set autoincrement value on non-Int field. SQL=%s autoIncrIdx=%d autoIncrFieldName=%s", bi.query, bi.autoIncrIdx, bi.autoIncrFieldName) } case TargetedAutoIncrInserter: - err := inserter.InsertAutoIncrToTarget(exec, bi.query, f.Addr().Interface(), bi.args...) + err := inserter.InsertAutoIncrToTarget(ctx, exec, bi.query, f.Addr().Interface(), bi.args...) if err != nil { return err } @@ -599,7 +565,7 @@ func insert(m *DbMap, exec SqlExecutor, list ...interface{}) error { if idQuery == "" { return fmt.Errorf("gorp: cannot set %s value if its ColumnMap.GeneratedIdQuery is empty", bi.autoIncrFieldName) } - err := inserter.InsertQueryToTarget(exec, bi.query, idQuery, f.Addr().Interface(), bi.args...) + err := inserter.InsertQueryToTarget(ctx, exec, bi.query, idQuery, f.Addr().Interface(), bi.args...) if err != nil { return err } @@ -607,7 +573,7 @@ func insert(m *DbMap, exec SqlExecutor, list ...interface{}) error { return fmt.Errorf("gorp: cannot use autoincrement fields on dialects that do not implement an autoincrementing interface") } } else { - _, err := exec.Exec(bi.query, bi.args...) + _, err := exec.ExecContext(ctx, bi.query, bi.args...) if err != nil { return err } @@ -622,51 +588,3 @@ func insert(m *DbMap, exec SqlExecutor, list ...interface{}) error { } return nil } - -func exec(e SqlExecutor, query string, args ...interface{}) (sql.Result, error) { - executor, ctx := extractExecutorAndContext(e) - - if ctx != nil { - return executor.ExecContext(ctx, query, args...) - } - - return executor.Exec(query, args...) -} - -func prepare(e SqlExecutor, query string) (*sql.Stmt, error) { - executor, ctx := extractExecutorAndContext(e) - - if ctx != nil { - return executor.PrepareContext(ctx, query) - } - - return executor.Prepare(query) -} - -func queryRow(e SqlExecutor, query string, args ...interface{}) *sql.Row { - executor, ctx := extractExecutorAndContext(e) - - if ctx != nil { - return executor.QueryRowContext(ctx, query, args...) - } - - return executor.QueryRow(query, args...) -} - -func query(e SqlExecutor, query string, args ...interface{}) (*sql.Rows, error) { - executor, ctx := extractExecutorAndContext(e) - - if ctx != nil { - return executor.QueryContext(ctx, query, args...) - } - - return executor.Query(query, args...) -} - -func begin(m *DbMap) (*sql.Tx, error) { - if m.ctx != nil { - return m.Db.BeginTx(m.ctx, nil) - } - - return m.Db.Begin() -} diff --git a/vendor/github.com/go-gorp/gorp/v3/hooks.go b/vendor/github.com/letsencrypt/borp/hooks.go similarity index 99% rename from vendor/github.com/go-gorp/gorp/v3/hooks.go rename to vendor/github.com/letsencrypt/borp/hooks.go index 1e80bca7e..5af75d2f5 100644 --- a/vendor/github.com/go-gorp/gorp/v3/hooks.go +++ b/vendor/github.com/letsencrypt/borp/hooks.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gorp +package borp //++ TODO v2-phase3: HasPostGet => PostGetter, HasPostDelete => PostDeleter, etc. diff --git a/vendor/github.com/go-gorp/gorp/v3/index.go b/vendor/github.com/letsencrypt/borp/index.go similarity index 95% rename from vendor/github.com/go-gorp/gorp/v3/index.go rename to vendor/github.com/letsencrypt/borp/index.go index df1cf5520..9cdaa08a8 100644 --- a/vendor/github.com/go-gorp/gorp/v3/index.go +++ b/vendor/github.com/letsencrypt/borp/index.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gorp +package borp // IndexMap represents a mapping between a Go struct field and a single // index in a table. @@ -17,7 +17,6 @@ type IndexMap struct { Unique bool // Index type supported by Dialect - // Postgres: B-tree, Hash, GiST and GIN. // Mysql: Btree, Hash. // Sqlite: nil. IndexType string @@ -29,7 +28,6 @@ type IndexMap struct { // Rename allows you to specify the index name in the table // // Example: table.IndMap("customer_test_idx").Rename("customer_idx") -// func (idx *IndexMap) Rename(indname string) *IndexMap { idx.IndexName = indname return idx diff --git a/vendor/github.com/go-gorp/gorp/v3/lockerror.go b/vendor/github.com/letsencrypt/borp/lockerror.go similarity index 90% rename from vendor/github.com/go-gorp/gorp/v3/lockerror.go rename to vendor/github.com/letsencrypt/borp/lockerror.go index 7e81891e2..0cba4c2b0 100644 --- a/vendor/github.com/go-gorp/gorp/v3/lockerror.go +++ b/vendor/github.com/letsencrypt/borp/lockerror.go @@ -2,9 +2,10 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gorp +package borp import ( + "context" "fmt" "reflect" ) @@ -39,11 +40,11 @@ func (e OptimisticLockError) Error() string { return fmt.Sprintf("gorp: OptimisticLockError no row found for table=%s keys=%v", e.TableName, e.Keys) } -func lockError(m *DbMap, exec SqlExecutor, tableName string, +func lockError(ctx context.Context, m *DbMap, exec SqlExecutor, tableName string, existingVer int64, elem reflect.Value, keys ...interface{}) (int64, error) { - existing, err := get(m, exec, elem.Interface(), keys...) + existing, err := get(ctx, m, exec, elem.Interface(), keys...) if err != nil { return -1, err } diff --git a/vendor/github.com/go-gorp/gorp/v3/logging.go b/vendor/github.com/letsencrypt/borp/logging.go similarity index 98% rename from vendor/github.com/go-gorp/gorp/v3/logging.go rename to vendor/github.com/letsencrypt/borp/logging.go index e8cba3db2..58863c5e2 100644 --- a/vendor/github.com/go-gorp/gorp/v3/logging.go +++ b/vendor/github.com/letsencrypt/borp/logging.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gorp +package borp import "fmt" diff --git a/vendor/github.com/go-gorp/gorp/v3/nulltypes.go b/vendor/github.com/letsencrypt/borp/nulltypes.go similarity index 98% rename from vendor/github.com/go-gorp/gorp/v3/nulltypes.go rename to vendor/github.com/letsencrypt/borp/nulltypes.go index 87b21f83f..df25a0294 100644 --- a/vendor/github.com/go-gorp/gorp/v3/nulltypes.go +++ b/vendor/github.com/letsencrypt/borp/nulltypes.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gorp +package borp import ( "database/sql/driver" diff --git a/vendor/github.com/go-gorp/gorp/v3/select.go b/vendor/github.com/letsencrypt/borp/select.go similarity index 80% rename from vendor/github.com/go-gorp/gorp/v3/select.go rename to vendor/github.com/letsencrypt/borp/select.go index 2d2d59618..4217fc797 100644 --- a/vendor/github.com/go-gorp/gorp/v3/select.go +++ b/vendor/github.com/letsencrypt/borp/select.go @@ -2,9 +2,10 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gorp +package borp import ( + "context" "database/sql" "fmt" "reflect" @@ -13,9 +14,9 @@ import ( // SelectInt executes the given query, which should be a SELECT statement for a single // integer column, and returns the value of the first row returned. If no rows are // found, zero is returned. -func SelectInt(e SqlExecutor, query string, args ...interface{}) (int64, error) { +func SelectInt(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (int64, error) { var h int64 - err := selectVal(e, &h, query, args...) + err := selectVal(ctx, e, &h, query, args...) if err != nil && err != sql.ErrNoRows { return 0, err } @@ -25,9 +26,9 @@ func SelectInt(e SqlExecutor, query string, args ...interface{}) (int64, error) // SelectNullInt executes the given query, which should be a SELECT statement for a single // integer column, and returns the value of the first row returned. If no rows are // found, the empty sql.NullInt64 value is returned. -func SelectNullInt(e SqlExecutor, query string, args ...interface{}) (sql.NullInt64, error) { +func SelectNullInt(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (sql.NullInt64, error) { var h sql.NullInt64 - err := selectVal(e, &h, query, args...) + err := selectVal(ctx, e, &h, query, args...) if err != nil && err != sql.ErrNoRows { return h, err } @@ -37,9 +38,9 @@ func SelectNullInt(e SqlExecutor, query string, args ...interface{}) (sql.NullIn // SelectFloat executes the given query, which should be a SELECT statement for a single // float column, and returns the value of the first row returned. If no rows are // found, zero is returned. -func SelectFloat(e SqlExecutor, query string, args ...interface{}) (float64, error) { +func SelectFloat(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (float64, error) { var h float64 - err := selectVal(e, &h, query, args...) + err := selectVal(ctx, e, &h, query, args...) if err != nil && err != sql.ErrNoRows { return 0, err } @@ -49,9 +50,9 @@ func SelectFloat(e SqlExecutor, query string, args ...interface{}) (float64, err // SelectNullFloat executes the given query, which should be a SELECT statement for a single // float column, and returns the value of the first row returned. If no rows are // found, the empty sql.NullInt64 value is returned. -func SelectNullFloat(e SqlExecutor, query string, args ...interface{}) (sql.NullFloat64, error) { +func SelectNullFloat(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (sql.NullFloat64, error) { var h sql.NullFloat64 - err := selectVal(e, &h, query, args...) + err := selectVal(ctx, e, &h, query, args...) if err != nil && err != sql.ErrNoRows { return h, err } @@ -61,9 +62,9 @@ func SelectNullFloat(e SqlExecutor, query string, args ...interface{}) (sql.Null // SelectStr executes the given query, which should be a SELECT statement for a single // char/varchar column, and returns the value of the first row returned. If no rows are // found, an empty string is returned. -func SelectStr(e SqlExecutor, query string, args ...interface{}) (string, error) { +func SelectStr(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (string, error) { var h string - err := selectVal(e, &h, query, args...) + err := selectVal(ctx, e, &h, query, args...) if err != nil && err != sql.ErrNoRows { return "", err } @@ -74,9 +75,9 @@ func SelectStr(e SqlExecutor, query string, args ...interface{}) (string, error) // statement for a single char/varchar column, and returns the value // of the first row returned. If no rows are found, the empty // sql.NullString is returned. -func SelectNullStr(e SqlExecutor, query string, args ...interface{}) (sql.NullString, error) { +func SelectNullStr(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (sql.NullString, error) { var h sql.NullString - err := selectVal(e, &h, query, args...) + err := selectVal(ctx, e, &h, query, args...) if err != nil && err != sql.ErrNoRows { return h, err } @@ -86,11 +87,10 @@ func SelectNullStr(e SqlExecutor, query string, args ...interface{}) (sql.NullSt // SelectOne executes the given query (which should be a SELECT statement) // and binds the result to holder, which must be a pointer. // -// If no row is found, an error (sql.ErrNoRows specifically) will be returned +// If no row is found, an error (sql.ErrNoRows specifically) will be returned. // // If more than one row is found, an error will be returned. -// -func SelectOne(m *DbMap, e SqlExecutor, holder interface{}, query string, args ...interface{}) error { +func SelectOne(ctx context.Context, m *DbMap, e SqlExecutor, holder interface{}, query string, args ...interface{}) error { t := reflect.TypeOf(holder) if t.Kind() == reflect.Ptr { t = t.Elem() @@ -108,7 +108,7 @@ func SelectOne(m *DbMap, e SqlExecutor, holder interface{}, query string, args . if t.Kind() == reflect.Struct { var nonFatalErr error - list, err := hookedselect(m, e, holder, query, args...) + list, err := hookedselect(ctx, m, e, holder, query, args...) if err != nil { if !NonFatalError(err) { // FIXME: double negative, rename NonFatalError to FatalError return err @@ -143,10 +143,10 @@ func SelectOne(m *DbMap, e SqlExecutor, holder interface{}, query string, args . return nonFatalErr } - return selectVal(e, holder, query, args...) + return selectVal(ctx, e, holder, query, args...) } -func selectVal(e SqlExecutor, holder interface{}, query string, args ...interface{}) error { +func selectVal(ctx context.Context, e SqlExecutor, holder interface{}, query string, args ...interface{}) error { if len(args) == 1 { switch m := e.(type) { case *DbMap: @@ -155,7 +155,7 @@ func selectVal(e SqlExecutor, holder interface{}, query string, args ...interfac query, args = maybeExpandNamedQuery(m.dbmap, query, args) } } - rows, err := e.Query(query, args...) + rows, err := e.QueryContext(ctx, query, args...) if err != nil { return err } @@ -168,15 +168,20 @@ func selectVal(e SqlExecutor, holder interface{}, query string, args ...interfac return sql.ErrNoRows } - return rows.Scan(holder) + err = rows.Scan(holder) + if err != nil { + return err + } + + return rows.Close() } -func hookedselect(m *DbMap, exec SqlExecutor, i interface{}, query string, +func hookedselect(ctx context.Context, m *DbMap, exec SqlExecutor, i interface{}, query string, args ...interface{}) ([]interface{}, error) { var nonFatalErr error - list, err := rawselect(m, exec, i, query, args...) + list, err := rawselect(ctx, m, exec, i, query, args...) if err != nil { if !NonFatalError(err) { return nil, err @@ -208,7 +213,7 @@ func hookedselect(m *DbMap, exec SqlExecutor, i interface{}, query string, return list, nonFatalErr } -func rawselect(m *DbMap, exec SqlExecutor, i interface{}, query string, +func rawselect(ctx context.Context, m *DbMap, exec SqlExecutor, i interface{}, query string, args ...interface{}) ([]interface{}, error) { var ( appendToSlice = false // Write results to i directly? @@ -251,7 +256,7 @@ func rawselect(m *DbMap, exec SqlExecutor, i interface{}, query string, } // Run the query - rows, err := exec.Query(query, args...) + rows, err := exec.QueryContext(ctx, query, args...) if err != nil { return nil, err } @@ -351,6 +356,11 @@ func rawselect(m *DbMap, exec SqlExecutor, i interface{}, query string, } } + err = rows.Close() + if err != nil { + return nil, err + } + if appendToSlice && sliceValue.IsNil() { sliceValue.Set(reflect.MakeSlice(sliceValue.Type(), 0, 0)) } diff --git a/vendor/github.com/go-gorp/gorp/v3/table.go b/vendor/github.com/letsencrypt/borp/table.go similarity index 96% rename from vendor/github.com/go-gorp/gorp/v3/table.go rename to vendor/github.com/letsencrypt/borp/table.go index 5931b2d9e..f4b713894 100644 --- a/vendor/github.com/go-gorp/gorp/v3/table.go +++ b/vendor/github.com/letsencrypt/borp/table.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gorp +package borp import ( "bytes" @@ -47,7 +47,6 @@ func (t *TableMap) ResetSql() { // Automatically calls ResetSql() to ensure SQL statements are regenerated. // // Panics if isAutoIncr is true, and fieldNames length != 1 -// func (t *TableMap) SetKeys(isAutoIncr bool, fieldNames ...string) *TableMap { if isAutoIncr && len(fieldNames) != 1 { panic(fmt.Sprintf( @@ -73,17 +72,13 @@ func (t *TableMap) SetKeys(isAutoIncr bool, fieldNames ...string) *TableMap { // Automatically calls ResetSql() to ensure SQL statements are regenerated. // // Panics if fieldNames length < 2. -// func (t *TableMap) SetUniqueTogether(fieldNames ...string) *TableMap { if len(fieldNames) < 2 { - panic(fmt.Sprintf( - "gorp: SetUniqueTogether: must provide at least two fieldNames to set uniqueness constraint.")) + panic("gorp: SetUniqueTogether: must provide at least two fieldNames to set uniqueness constraint.") } columns := make([]string, 0, len(fieldNames)) - for _, name := range fieldNames { - columns = append(columns, name) - } + columns = append(columns, fieldNames...) for _, existingColumns := range t.uniqueTogether { if equal(existingColumns, columns) { @@ -135,7 +130,6 @@ func (t *TableMap) IdxMap(field string) *IndexMap { // Function will panic if one of the given for index columns does not exists // // Automatically calls ResetSql() to ensure SQL statements are regenerated. -// func (t *TableMap) AddIndex(name string, idxtype string, columns []string) *IndexMap { // check if we have a index with this name already for _, idx := range t.indexes { diff --git a/vendor/github.com/go-gorp/gorp/v3/table_bindings.go b/vendor/github.com/letsencrypt/borp/table_bindings.go similarity index 99% rename from vendor/github.com/go-gorp/gorp/v3/table_bindings.go rename to vendor/github.com/letsencrypt/borp/table_bindings.go index 43c0b3dd4..6c2f146e6 100644 --- a/vendor/github.com/go-gorp/gorp/v3/table_bindings.go +++ b/vendor/github.com/letsencrypt/borp/table_bindings.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gorp +package borp import ( "bytes" diff --git a/vendor/github.com/go-gorp/gorp/v3/test_all.sh b/vendor/github.com/letsencrypt/borp/test_all.sh similarity index 68% rename from vendor/github.com/go-gorp/gorp/v3/test_all.sh rename to vendor/github.com/letsencrypt/borp/test_all.sh index 91007d645..c3dd4240d 100644 --- a/vendor/github.com/go-gorp/gorp/v3/test_all.sh +++ b/vendor/github.com/letsencrypt/borp/test_all.sh @@ -6,11 +6,6 @@ echo "Running unit tests" go test -race -echo "Testing against postgres" -export GORP_TEST_DSN="host=postgres user=gorptest password=gorptest dbname=gorptest sslmode=disable" -export GORP_TEST_DIALECT=postgres -go test -tags integration $GOBUILDFLAG $@ . - echo "Testing against sqlite" export GORP_TEST_DSN=/tmp/gorptest.bin export GORP_TEST_DIALECT=sqlite diff --git a/vendor/github.com/go-gorp/gorp/v3/transaction.go b/vendor/github.com/letsencrypt/borp/transaction.go similarity index 53% rename from vendor/github.com/go-gorp/gorp/v3/transaction.go rename to vendor/github.com/letsencrypt/borp/transaction.go index d505d94c9..27eecc43f 100644 --- a/vendor/github.com/go-gorp/gorp/v3/transaction.go +++ b/vendor/github.com/letsencrypt/borp/transaction.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package gorp +package borp import ( "context" @@ -15,55 +15,47 @@ import ( // of that transaction. Transactions should be terminated with // a call to Commit() or Rollback() type Transaction struct { - ctx context.Context dbmap *DbMap tx *sql.Tx closed bool } -func (t *Transaction) WithContext(ctx context.Context) SqlExecutor { - copy := &Transaction{} - *copy = *t - copy.ctx = ctx - return copy -} - // Insert has the same behavior as DbMap.Insert(), but runs in a transaction. -func (t *Transaction) Insert(list ...interface{}) error { - return insert(t.dbmap, t, list...) +func (t *Transaction) Insert(ctx context.Context, list ...interface{}) error { + return insert(ctx, t.dbmap, t, list...) } // Update had the same behavior as DbMap.Update(), but runs in a transaction. -func (t *Transaction) Update(list ...interface{}) (int64, error) { - return update(t.dbmap, t, nil, list...) +func (t *Transaction) Update(ctx context.Context, list ...interface{}) (int64, error) { + return update(ctx, t.dbmap, t, nil, list...) } // UpdateColumns had the same behavior as DbMap.UpdateColumns(), but runs in a transaction. -func (t *Transaction) UpdateColumns(filter ColumnFilter, list ...interface{}) (int64, error) { - return update(t.dbmap, t, filter, list...) +func (t *Transaction) UpdateColumns(ctx context.Context, filter ColumnFilter, list ...interface{}) (int64, error) { + return update(ctx, t.dbmap, t, filter, list...) } // Delete has the same behavior as DbMap.Delete(), but runs in a transaction. -func (t *Transaction) Delete(list ...interface{}) (int64, error) { - return delete(t.dbmap, t, list...) +func (t *Transaction) Delete(ctx context.Context, list ...interface{}) (int64, error) { + return delete(ctx, t.dbmap, t, list...) } // Get has the same behavior as DbMap.Get(), but runs in a transaction. -func (t *Transaction) Get(i interface{}, keys ...interface{}) (interface{}, error) { - return get(t.dbmap, t, i, keys...) +func (t *Transaction) Get(ctx context.Context, i interface{}, keys ...interface{}) (interface{}, error) { + return get(ctx, t.dbmap, t, i, keys...) } // Select has the same behavior as DbMap.Select(), but runs in a transaction. -func (t *Transaction) Select(i interface{}, query string, args ...interface{}) ([]interface{}, error) { +func (t *Transaction) Select(ctx context.Context, i interface{}, query string, args ...interface{}) ([]interface{}, error) { if t.dbmap.ExpandSliceArgs { expandSliceArgs(&query, args...) } - return hookedselect(t.dbmap, t, i, query, args...) + return hookedselect(ctx, t.dbmap, t, i, query, args...) } -// Exec has the same behavior as DbMap.Exec(), but runs in a transaction. -func (t *Transaction) Exec(query string, args ...interface{}) (sql.Result, error) { +// ExecContext has the same behavior as DbMap.ExecContext(), but runs in a transaction. +func (t *Transaction) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { if t.dbmap.ExpandSliceArgs { expandSliceArgs(&query, args...) } @@ -72,70 +64,70 @@ func (t *Transaction) Exec(query string, args ...interface{}) (sql.Result, error now := time.Now() defer t.dbmap.trace(now, query, args...) } - return maybeExpandNamedQueryAndExec(t, query, args...) + return maybeExpandNamedQueryAndExec(ctx, t, query, args...) } -// SelectInt is a convenience wrapper around the gorp.SelectInt function. -func (t *Transaction) SelectInt(query string, args ...interface{}) (int64, error) { +// SelectInt is a convenience wrapper around the borp.SelectInt function. +func (t *Transaction) SelectInt(ctx context.Context, query string, args ...interface{}) (int64, error) { if t.dbmap.ExpandSliceArgs { expandSliceArgs(&query, args...) } - return SelectInt(t, query, args...) + return SelectInt(ctx, t, query, args...) } -// SelectNullInt is a convenience wrapper around the gorp.SelectNullInt function. -func (t *Transaction) SelectNullInt(query string, args ...interface{}) (sql.NullInt64, error) { +// SelectNullInt is a convenience wrapper around the borp.SelectNullInt function. +func (t *Transaction) SelectNullInt(ctx context.Context, query string, args ...interface{}) (sql.NullInt64, error) { if t.dbmap.ExpandSliceArgs { expandSliceArgs(&query, args...) } - return SelectNullInt(t, query, args...) + return SelectNullInt(ctx, t, query, args...) } -// SelectFloat is a convenience wrapper around the gorp.SelectFloat function. -func (t *Transaction) SelectFloat(query string, args ...interface{}) (float64, error) { +// SelectFloat is a convenience wrapper around the borp.SelectFloat function. +func (t *Transaction) SelectFloat(ctx context.Context, query string, args ...interface{}) (float64, error) { if t.dbmap.ExpandSliceArgs { expandSliceArgs(&query, args...) } - return SelectFloat(t, query, args...) + return SelectFloat(ctx, t, query, args...) } -// SelectNullFloat is a convenience wrapper around the gorp.SelectNullFloat function. -func (t *Transaction) SelectNullFloat(query string, args ...interface{}) (sql.NullFloat64, error) { +// SelectNullFloat is a convenience wrapper around the borp.SelectNullFloat function. +func (t *Transaction) SelectNullFloat(ctx context.Context, query string, args ...interface{}) (sql.NullFloat64, error) { if t.dbmap.ExpandSliceArgs { expandSliceArgs(&query, args...) } - return SelectNullFloat(t, query, args...) + return SelectNullFloat(ctx, t, query, args...) } -// SelectStr is a convenience wrapper around the gorp.SelectStr function. -func (t *Transaction) SelectStr(query string, args ...interface{}) (string, error) { +// SelectStr is a convenience wrapper around the borp.SelectStr function. +func (t *Transaction) SelectStr(ctx context.Context, query string, args ...interface{}) (string, error) { if t.dbmap.ExpandSliceArgs { expandSliceArgs(&query, args...) } - return SelectStr(t, query, args...) + return SelectStr(ctx, t, query, args...) } -// SelectNullStr is a convenience wrapper around the gorp.SelectNullStr function. -func (t *Transaction) SelectNullStr(query string, args ...interface{}) (sql.NullString, error) { +// SelectNullStr is a convenience wrapper around the borp.SelectNullStr function. +func (t *Transaction) SelectNullStr(ctx context.Context, query string, args ...interface{}) (sql.NullString, error) { if t.dbmap.ExpandSliceArgs { expandSliceArgs(&query, args...) } - return SelectNullStr(t, query, args...) + return SelectNullStr(ctx, t, query, args...) } -// SelectOne is a convenience wrapper around the gorp.SelectOne function. -func (t *Transaction) SelectOne(holder interface{}, query string, args ...interface{}) error { +// SelectOne is a convenience wrapper around the borp.SelectOne function. +func (t *Transaction) SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error { if t.dbmap.ExpandSliceArgs { expandSliceArgs(&query, args...) } - return SelectOne(t.dbmap, t, holder, query, args...) + return SelectOne(ctx, t.dbmap, t, holder, query, args...) } // Commit commits the underlying database transaction. @@ -169,52 +161,52 @@ func (t *Transaction) Rollback() error { // Savepoint creates a savepoint with the given name. The name is interpolated // directly into the SQL SAVEPOINT statement, so you must sanitize it if it is // derived from user input. -func (t *Transaction) Savepoint(name string) error { +func (t *Transaction) Savepoint(ctx context.Context, name string) error { query := "savepoint " + t.dbmap.Dialect.QuoteField(name) if t.dbmap.logger != nil { now := time.Now() defer t.dbmap.trace(now, query, nil) } - _, err := exec(t, query) + _, err := t.ExecContext(ctx, query) return err } // RollbackToSavepoint rolls back to the savepoint with the given name. The // name is interpolated directly into the SQL SAVEPOINT statement, so you must // sanitize it if it is derived from user input. -func (t *Transaction) RollbackToSavepoint(savepoint string) error { +func (t *Transaction) RollbackToSavepoint(ctx context.Context, savepoint string) error { query := "rollback to savepoint " + t.dbmap.Dialect.QuoteField(savepoint) if t.dbmap.logger != nil { now := time.Now() defer t.dbmap.trace(now, query, nil) } - _, err := exec(t, query) + _, err := t.ExecContext(ctx, query) return err } // ReleaseSavepint releases the savepoint with the given name. The name is // interpolated directly into the SQL SAVEPOINT statement, so you must sanitize // it if it is derived from user input. -func (t *Transaction) ReleaseSavepoint(savepoint string) error { +func (t *Transaction) ReleaseSavepoint(ctx context.Context, savepoint string) error { query := "release savepoint " + t.dbmap.Dialect.QuoteField(savepoint) if t.dbmap.logger != nil { now := time.Now() defer t.dbmap.trace(now, query, nil) } - _, err := exec(t, query) + _, err := t.ExecContext(ctx, query) return err } // Prepare has the same behavior as DbMap.Prepare(), but runs in a transaction. -func (t *Transaction) Prepare(query string) (*sql.Stmt, error) { +func (t *Transaction) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) { if t.dbmap.logger != nil { now := time.Now() defer t.dbmap.trace(now, query, nil) } - return prepare(t, query) + return t.tx.PrepareContext(ctx, query) } -func (t *Transaction) QueryRow(query string, args ...interface{}) *sql.Row { +func (t *Transaction) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row { if t.dbmap.ExpandSliceArgs { expandSliceArgs(&query, args...) } @@ -223,10 +215,10 @@ func (t *Transaction) QueryRow(query string, args ...interface{}) *sql.Row { now := time.Now() defer t.dbmap.trace(now, query, args...) } - return queryRow(t, query, args...) + return t.tx.QueryRowContext(ctx, query, args...) } -func (t *Transaction) Query(q string, args ...interface{}) (*sql.Rows, error) { +func (t *Transaction) QueryContext(ctx context.Context, q string, args ...interface{}) (*sql.Rows, error) { if t.dbmap.ExpandSliceArgs { expandSliceArgs(&q, args...) } @@ -235,5 +227,5 @@ func (t *Transaction) Query(q string, args ...interface{}) (*sql.Rows, error) { now := time.Now() defer t.dbmap.trace(now, q, args...) } - return query(t, q, args...) + return t.tx.QueryContext(ctx, q, args...) } diff --git a/vendor/modules.txt b/vendor/modules.txt index ab0eacf4c..efc6bee55 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -132,9 +132,6 @@ github.com/eggsampler/acme/v3 # github.com/felixge/httpsnoop v1.0.3 ## explicit; go 1.13 github.com/felixge/httpsnoop -# github.com/go-gorp/gorp/v3 v3.1.0 -## explicit; go 1.18 -github.com/go-gorp/gorp/v3 # github.com/go-logr/logr v1.2.4 ## explicit; go 1.16 github.com/go-logr/logr @@ -204,6 +201,11 @@ github.com/hpcloud/tail/winfile # github.com/jmhodges/clock v1.2.0 ## explicit; go 1.17 github.com/jmhodges/clock +# github.com/kr/pretty v0.3.1 +## explicit; go 1.12 +# github.com/letsencrypt/borp v0.0.0-20230707160741-6cc6ce580243 +## explicit; go 1.20 +github.com/letsencrypt/borp # github.com/letsencrypt/challtestsrv v1.2.1 ## explicit; go 1.13 github.com/letsencrypt/challtestsrv @@ -225,6 +227,8 @@ github.com/miekg/pkcs11 # github.com/pelletier/go-toml v1.9.3 ## explicit; go 1.12 github.com/pelletier/go-toml +# github.com/poy/onpar v1.1.2 +## explicit; go 1.14 # github.com/prometheus/client_golang v1.14.0 ## explicit; go 1.17 github.com/prometheus/client_golang/prometheus @@ -244,8 +248,6 @@ github.com/prometheus/common/model github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util -# github.com/rogpeppe/go-internal v1.9.0 -## explicit; go 1.17 # github.com/syndtr/goleveldb v1.0.0 ## explicit github.com/syndtr/goleveldb/leveldb @@ -513,6 +515,8 @@ google.golang.org/protobuf/types/known/fieldmaskpb google.golang.org/protobuf/types/known/structpb google.golang.org/protobuf/types/known/timestamppb google.golang.org/protobuf/types/known/wrapperspb +# gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c +## explicit; go 1.11 # gopkg.in/fsnotify.v1 v1.4.7 ## explicit gopkg.in/fsnotify.v1