sa: use internal certificateStatusModel instead of core.CertificateStatus (#8159)

Part of https://github.com/letsencrypt/boulder/issues/8112
This commit is contained in:
Jacob Hoffman-Andrews 2025-05-12 14:53:08 -07:00 committed by GitHub
parent 01a299cd0f
commit 388c68cb49
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 76 additions and 74 deletions

View File

@ -516,15 +516,18 @@ func insertCertificate(cert certDERWithRegID, lastNagSent time.Time) error {
return fmt.Errorf("inserting certificate: %w", err)
}
return setupDBMap.Insert(ctx, &core.CertificateStatus{
Serial: core.SerialToString(parsedCert.SerialNumber),
LastExpirationNagSent: lastNagSent,
Status: core.OCSPStatusGood,
NotAfter: parsedCert.NotAfter,
OCSPLastUpdated: time.Time{},
RevokedDate: time.Time{},
RevokedReason: 0,
})
_, err = setupDBMap.ExecContext(context.Background(),
`INSERT INTO certificateStatus
(serial, notAfter, status, ocspLastUpdated, revokedDate, revokedReason, lastExpirationNagSent)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
core.SerialToString(parsedCert.SerialNumber),
parsedCert.NotAfter,
core.OCSPStatusGood,
time.Time{},
time.Time{},
0,
lastNagSent)
return err
}
func addExpiringCerts(t *testing.T, ctx *testCtx) []certDERWithRegID {

View File

@ -15,6 +15,7 @@ import (
capb "github.com/letsencrypt/boulder/ca/proto"
"github.com/letsencrypt/boulder/cmd"
"github.com/letsencrypt/boulder/core"
"github.com/letsencrypt/boulder/db"
blog "github.com/letsencrypt/boulder/log"
"github.com/letsencrypt/boulder/metrics"
"github.com/letsencrypt/boulder/rocsp"
@ -50,29 +51,34 @@ func makeClient() (*rocsp.RWClient, clock.Clock) {
return rocsp.NewWritingClient(rdb, 500*time.Millisecond, clk, metrics.NoopRegisterer), clk
}
func TestGetStartingID(t *testing.T) {
ctx := context.Background()
func insertCertificateStatus(t *testing.T, dbMap db.Executor, serial string, notAfter, ocspLastUpdated time.Time) int64 {
result, err := dbMap.ExecContext(context.Background(),
`INSERT INTO certificateStatus
(serial, notAfter, status, ocspLastUpdated, revokedDate, revokedReason, lastExpirationNagSent, issuerID)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
serial,
notAfter,
core.OCSPStatusGood,
ocspLastUpdated,
time.Time{},
0,
time.Time{},
99)
test.AssertNotError(t, err, "inserting certificate status")
id, err := result.LastInsertId()
test.AssertNotError(t, err, "getting last insert ID")
return id
}
func TestGetStartingID(t *testing.T) {
clk := clock.NewFake()
dbMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms)
test.AssertNotError(t, err, "failed setting up db client")
defer test.ResetBoulderTestDatabase(t)()
cs := core.CertificateStatus{
Serial: "1337",
NotAfter: clk.Now().Add(12 * time.Hour),
}
err = dbMap.Insert(ctx, &cs)
test.AssertNotError(t, err, "inserting certificate status")
firstID := cs.ID
firstID := insertCertificateStatus(t, dbMap, "1337", clk.Now().Add(12*time.Hour), time.Time{})
secondID := insertCertificateStatus(t, dbMap, "1338", clk.Now().Add(36*time.Hour), time.Time{})
cs = core.CertificateStatus{
Serial: "1338",
NotAfter: clk.Now().Add(36 * time.Hour),
}
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)
clk.Sleep(48 * time.Hour)
@ -131,11 +137,7 @@ func TestLoadFromDB(t *testing.T) {
defer test.ResetBoulderTestDatabase(t)
for i := range 100 {
err = dbMap.Insert(context.Background(), &core.CertificateStatus{
Serial: fmt.Sprintf("%036x", i),
NotAfter: clk.Now().Add(200 * time.Hour),
OCSPLastUpdated: clk.Now(),
})
insertCertificateStatus(t, dbMap, fmt.Sprintf("%036x", i), clk.Now().Add(200*time.Hour), clk.Now())
if err != nil {
t.Fatalf("Failed to insert certificateStatus: %s", err)
}

View File

@ -18,7 +18,6 @@ import (
corepb "github.com/letsencrypt/boulder/core/proto"
"github.com/letsencrypt/boulder/identifier"
"github.com/letsencrypt/boulder/probs"
"github.com/letsencrypt/boulder/revocation"
sapb "github.com/letsencrypt/boulder/sa/proto"
vapb "github.com/letsencrypt/boulder/va/proto"
)
@ -343,34 +342,6 @@ func newOrderValid(order *corepb.Order) bool {
return !(order.RegistrationID == 0 || order.Expires == nil || len(order.Identifiers) == 0)
}
func CertStatusToPB(certStatus core.CertificateStatus) *corepb.CertificateStatus {
return &corepb.CertificateStatus{
Serial: certStatus.Serial,
Status: string(certStatus.Status),
OcspLastUpdated: timestamppb.New(certStatus.OCSPLastUpdated),
RevokedDate: timestamppb.New(certStatus.RevokedDate),
RevokedReason: int64(certStatus.RevokedReason),
LastExpirationNagSent: timestamppb.New(certStatus.LastExpirationNagSent),
NotAfter: timestamppb.New(certStatus.NotAfter),
IsExpired: certStatus.IsExpired,
IssuerID: certStatus.IssuerNameID,
}
}
func PBToCertStatus(pb *corepb.CertificateStatus) core.CertificateStatus {
return core.CertificateStatus{
Serial: pb.Serial,
Status: core.OCSPStatus(pb.Status),
OCSPLastUpdated: pb.OcspLastUpdated.AsTime(),
RevokedDate: pb.RevokedDate.AsTime(),
RevokedReason: revocation.Reason(pb.RevokedReason),
LastExpirationNagSent: pb.LastExpirationNagSent.AsTime(),
NotAfter: pb.NotAfter.AsTime(),
IsExpired: pb.IsExpired,
IssuerNameID: pb.IssuerID,
}
}
// PBToAuthzMap converts a protobuf map of domains mapped to protobuf authorizations to a
// golang map[string]*core.Authorization.
func PBToAuthzMap(pb *sapb.Authorizations) (map[identifier.ACMEIdentifier]*core.Authorization, error) {

View File

@ -270,7 +270,7 @@ func initTables(dbMap *borp.DbMap) {
regTable.ColMap("KeySHA256").SetNotNull(true).SetUnique(true)
dbMap.AddTableWithName(issuedNameModel{}, "issuedNames").SetKeys(true, "ID")
dbMap.AddTableWithName(core.Certificate{}, "certificates").SetKeys(true, "ID")
dbMap.AddTableWithName(core.CertificateStatus{}, "certificateStatus").SetKeys(true, "ID")
dbMap.AddTableWithName(certificateStatusModel{}, "certificateStatus").SetKeys(true, "ID")
dbMap.AddTableWithName(core.FQDNSet{}, "fqdnSets").SetKeys(true, "ID")
tableMap := dbMap.AddTableWithName(orderModel{}, "orders").SetKeys(true, "ID")
if !features.Get().StoreARIReplacesInOrders {

View File

@ -212,15 +212,15 @@ const certStatusFields = "id, serial, status, ocspLastUpdated, revokedDate, revo
// SelectCertificateStatus selects all fields of one certificate status model
// identified by serial
func SelectCertificateStatus(ctx context.Context, s db.OneSelector, serial string) (core.CertificateStatus, error) {
var model core.CertificateStatus
func SelectCertificateStatus(ctx context.Context, s db.OneSelector, serial string) (*corepb.CertificateStatus, error) {
var model certificateStatusModel
err := s.SelectOne(
ctx,
&model,
"SELECT "+certStatusFields+" FROM certificateStatus WHERE serial = ? LIMIT 1",
serial,
)
return model, err
return model.toPb(), err
}
// RevocationStatusModel represents a small subset of the columns in the
@ -393,6 +393,33 @@ func (model certificateModel) toPb() *corepb.Certificate {
}
}
type certificateStatusModel struct {
ID int64 `db:"id"`
Serial string `db:"serial"`
Status core.OCSPStatus `db:"status"`
OCSPLastUpdated time.Time `db:"ocspLastUpdated"`
RevokedDate time.Time `db:"revokedDate"`
RevokedReason revocation.Reason `db:"revokedReason"`
LastExpirationNagSent time.Time `db:"lastExpirationNagSent"`
NotAfter time.Time `db:"notAfter"`
IsExpired bool `db:"isExpired"`
IssuerID int64 `db:"issuerID"`
}
func (model certificateStatusModel) toPb() *corepb.CertificateStatus {
return &corepb.CertificateStatus{
Serial: model.Serial,
Status: string(model.Status),
OcspLastUpdated: timestamppb.New(model.OCSPLastUpdated),
RevokedDate: timestamppb.New(model.RevokedDate),
RevokedReason: int64(model.RevokedReason),
LastExpirationNagSent: timestamppb.New(model.LastExpirationNagSent),
NotAfter: timestamppb.New(model.NotAfter),
IsExpired: model.IsExpired,
IssuerID: model.IssuerID,
}
}
// orderModel represents one row in the orders table. The CertificateProfileName
// column is a pointer because the column is NULL-able.
type orderModel struct {

View File

@ -330,7 +330,7 @@ func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb
if req.OcspNotReady {
status = core.OCSPStatusNotReady
}
cs := &core.CertificateStatus{
cs := &certificateStatusModel{
Serial: serialHex,
Status: status,
OCSPLastUpdated: ssa.clk.Now(),
@ -339,7 +339,7 @@ func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb
LastExpirationNagSent: time.Time{},
NotAfter: parsed.NotAfter,
IsExpired: false,
IssuerNameID: req.IssuerNameID,
IssuerID: req.IssuerNameID,
}
err = ssa.dbMap.Insert(ctx, cs)
if err != nil {
@ -1374,9 +1374,9 @@ func (ssa *SQLStorageAuthority) PauseIdentifiers(ctx context.Context, req *sapb.
err := tx.SelectOne(ctx, &entry, `
SELECT pausedAt, unpausedAt
FROM paused
WHERE
registrationID = ? AND
identifierType = ? AND
WHERE
registrationID = ? AND
identifierType = ? AND
identifierValue = ?`,
req.RegistrationID,
ident.Type,
@ -1420,9 +1420,9 @@ func (ssa *SQLStorageAuthority) PauseIdentifiers(ctx context.Context, req *sapb.
UPDATE paused
SET pausedAt = ?,
unpausedAt = NULL
WHERE
registrationID = ? AND
identifierType = ? AND
WHERE
registrationID = ? AND
identifierType = ? AND
identifierValue = ? AND
unpausedAt IS NOT NULL`,
ssa.clk.Now().Truncate(time.Second),
@ -1468,7 +1468,7 @@ func (ssa *SQLStorageAuthority) UnpauseAccount(ctx context.Context, req *sapb.Re
result, err := ssa.dbMap.ExecContext(ctx, `
UPDATE paused
SET unpausedAt = ?
WHERE
WHERE
registrationID = ? AND
unpausedAt IS NULL
LIMIT ?`,

View File

@ -20,7 +20,6 @@ import (
corepb "github.com/letsencrypt/boulder/core/proto"
"github.com/letsencrypt/boulder/db"
berrors "github.com/letsencrypt/boulder/errors"
bgrpc "github.com/letsencrypt/boulder/grpc"
"github.com/letsencrypt/boulder/identifier"
blog "github.com/letsencrypt/boulder/log"
sapb "github.com/letsencrypt/boulder/sa/proto"
@ -258,7 +257,7 @@ func (ssa *SQLStorageAuthorityRO) GetCertificateStatus(ctx context.Context, req
return nil, err
}
return bgrpc.CertStatusToPB(certStatus), nil
return certStatus, nil
}
// GetRevocationStatus takes a hexadecimal string representing the full serial