in go/cmd/ct-fetch/ct-fetch.go [473:564]
func (ld *LogSyncEngine) NewLogWorker(ctx context.Context, ctLogMeta *types.CTLogMetadata) (*LogWorker, error) {
batchSize := *ctconfig.BatchSize
logUrlObj, err := url.Parse(ctLogMeta.URL)
if err != nil {
glog.Errorf("[%s] Unable to parse CT Log URL: %s", ctLogMeta.URL, err)
return nil, err
}
logObj, err := ld.database.GetLogState(logUrlObj)
if err != nil {
glog.Errorf("[%s] Unable to get cached CT Log state: %s", ctLogMeta.URL, err)
return nil, err
}
if logObj.LogID != ctLogMeta.LogID {
// The LogID shouldn't change, but we'll treat the input as
// authoritative. Old versions of ct-fetch didn't store the
// LogID in redis, so we will hit this on upgrade.
logObj.LogID = ctLogMeta.LogID
}
if logObj.MMD != uint64(ctLogMeta.MMD) {
// Likewise storing MMD is new.
logObj.MMD = uint64(ctLogMeta.MMD)
}
ctLog, err := client.New(ctLogMeta.URL, &httpClient, jsonclient.Options{
UserAgent: "ct-fetch; https://github.com/mozilla/crlite",
})
if err != nil {
glog.Errorf("[%s] Unable to construct CT log client: %s", ctLogMeta.URL, err)
return nil, err
}
glog.Infof("[%s] Fetching signed tree head... ", ctLogMeta.URL)
sth, fetchErr := ctLog.GetSTH(ctx)
if fetchErr == nil {
glog.Infof("[%s] %d total entries as of %s", ctLogMeta.URL, sth.TreeSize,
uint64ToTimestamp(sth.Timestamp).Format(time.ANSIC))
}
// Determine what the worker should do.
var task LogWorkerTask
if fetchErr != nil {
// Temporary network failure?
glog.Warningf("[%s] Unable to fetch signed tree head: %s", ctLogMeta.URL, fetchErr)
task = Sleep
} else if sth.TreeSize <= 3 {
// For technical reasons, we can't verify our download
// until there are at least 3 entries in the log. So
// we'll wait.
task = Sleep
} else if logObj.LastUpdateTime.IsZero() {
// First contact with log
task = Init
} else if logObj.MaxEntry+batchSize < sth.TreeSize {
// There are many new entries to download.
task = Update
} else if logObj.MinEntry > 0 {
// There are not many new entries, but there's a
// backlog of old entries. Prioritize the backlog.
task = Backfill
} else if time.Since(logObj.LastUpdateTime) < 10*time.Minute {
// There are few new entries, no old entries, and we updated
// recently. So sleep.
task = Sleep
} else if logObj.MaxEntry < sth.TreeSize-1 {
// There is at least one new entry and we haven't
// downloaded anything recently.
task = Update
} else {
// There are no new entries.
task = Sleep
}
metricKey := ctLogMeta.MetricKey()
if sth != nil {
metrics.SetGauge([]string{metricKey, "coverage"}, float32(logObj.MaxEntry-logObj.MinEntry+1)/float32(sth.TreeSize))
}
return &LogWorker{
Database: ld.database,
Client: ctLog,
LogState: logObj,
LogMeta: ctLogMeta,
STH: sth,
WorkOrder: task,
JobSize: batchSize,
MetricKey: metricKey,
}, nil
}