Files
trufflehog/pkg/sources/elasticsearch/unit_of_work.go
Charlie GunyonandCharlie Gunyon 311494e86e Elastic adapter (#2727)
* Add stub source and elastic API funcs

* Spawn workers and ship chunks

* Now successfully detects a credential

- Added tests
- Added some documentation comments
- Threaded the passed context through to all the API requests

* Linting fixes

* Add integration tests and resolve some bugs they uncovered

* Logstash -> Elasticsearch

* Add support for --index-pattern

* Add support for --query-json

* Use structs instead of string building to construct a search body

* Support --since-timestamp

* Implement additional authentication methods

* Fix some small bugs

* Refactoring to support --best-effort-scan

* Finish implementation of --best-effort-scan

* Implement scan catch-up

* Finish connecting support for nodes CLI arg

* Add some integration tests around the catchup mechanism

* go mod tidy

* Fix some linting issues

* Remove some debugging Prints

* Move off of _doc

* Remove informational Printf and add informational logging

* Remove debugging logging

* Copy the index from the outer loop as well

* Don't burn up the ES API with rapid requests if there's no work to do in subsequent scans

* No need to export UnitOfWork.AddSearch

* Use a better name for the range query variable when building the timestamp range clause in searches

* Replace some unlocking defers with explicit unlocks to make the synchronized part of the code clearer

* found -> ok

* Remove superfluous buildElasticClient method

---------

Co-authored-by: Charlie Gunyon <[email protected]>
2024-05-24 09:38:20 -05:00

102 lines
2.4 KiB
Go

package elasticsearch
import "fmt"
type DocumentSearch struct {
index *Index
offset int
documentCount int
skipCount int
filterParams *FilterParams
}
type UnitOfWork struct {
maxDocumentCount int
documentCount int
documentSearches []DocumentSearch
}
func NewUnitOfWork(maxDocumentCount int) UnitOfWork {
uow := UnitOfWork{maxDocumentCount: maxDocumentCount}
uow.documentSearches = []DocumentSearch{}
return uow
}
func (ds *DocumentSearch) String() string {
if ds.offset > 0 {
return fmt.Sprintf("%s [%d:]", ds.index.name, ds.offset)
} else {
return ds.index.name
}
}
func (uow *UnitOfWork) addSearch(
index *Index,
filterParams *FilterParams,
offset int,
scanCoverageRate float64,
) int {
indexDocCount := index.documentCount - offset
addedDocumentCount := min(uow.maxDocumentCount-uow.documentCount, indexDocCount)
if addedDocumentCount > 0 {
uow.documentSearches = append(uow.documentSearches, DocumentSearch{
index: index,
offset: offset,
documentCount: addedDocumentCount,
skipCount: int(float64(addedDocumentCount) * (1.0 - scanCoverageRate)),
filterParams: filterParams,
})
uow.documentCount += addedDocumentCount
}
return addedDocumentCount
}
func distributeDocumentScans(
indices *Indices,
maxUnits int,
scanCoverageRate float64,
) []UnitOfWork {
totalDocumentCount := 0
for _, i := range indices.indices {
totalDocumentCount += i.documentCount
}
unitsOfWork := make([]UnitOfWork, maxUnits)
documentsAssigned := 0
for i := 0; i < maxUnits; i++ {
documentCount := totalDocumentCount / maxUnits
// The total number of documents to process might not be perfectly
// divisible by the number of workers, so make sure any remaining documents
// get processed by assigning them to the last worker
if i == maxUnits-1 {
documentCount = totalDocumentCount - documentsAssigned
}
unitsOfWork[i] = NewUnitOfWork(documentCount)
documentsAssigned += documentCount
}
unitOfWorkIndex := 0
for _, i := range indices.indices {
uow := &unitsOfWork[unitOfWorkIndex]
offset := uow.addSearch(i, indices.filterParams, 0, scanCoverageRate)
// If we've yet to distribute all the documents in the index, go into the
// next unit of work, and the next, and the next....
for offset < i.documentCount {
unitOfWorkIndex++
uow := &unitsOfWork[unitOfWorkIndex]
offset += uow.addSearch(i, indices.filterParams, offset, scanCoverageRate)
}
}
return unitsOfWork
}