mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-31 09:20:50 +01:00
* filer: format ChunkView without its cipher key readChunkSliceAt logs a failed chunk fetch with %+v, which printed the per-chunk AES-256-GCM key into error-level logs on every failed read. Give ChunkView a String() that omits CipherKey so %v and %+v stay safe. * pb: scrub cipher keys when an entry must be logged whole ScrubCipherKeys clones an Entry with chunk CipherKey fields cleared, so a warning that needs the full entry still cannot leak the keys. * log entry identifiers instead of whole metadata messages Several log lines formatted whole protobuf messages — SubscribeMetadataResponse, EventNotification, Entry-carrying requests — whose chunks include cipher_key under -encryptVolumeData, so the keys escaped into logs (some at default verbosity). Log the directory, entry name and event timestamp instead; where a chunk list was printed, print file ids via FileIdsString. * log queue: unwrap SubscribeMetadataResponse and never print raw protos The fallback printed any message with %+v, so a SubscribeMetadataResponse carrying entries would still put cipher keys in the log. Unwrap its event notifications (batched events included) and reduce unknown messages to their type name. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * remote sync logs: name the event's real path Rename targets logged under resp.Directory while the entry lives under NewParentPath; route them through MetadataEventTargetDirectory. Sync failure logs printed only the directory, so same-dir failures shared an identifier — log the source and destination paths instead. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * log queue: log the entry's destination path on moves A cross-directory rename printed "file" -> "file" under the source path, hiding where the entry landed. Use NewParentPath so the log shows the destination directory next to the new name. * admin: install the go.mod-pinned templ, not @latest install-templ fetched templ@latest, which now emits templruntime.NewAttributeExpression - a symbol absent from the runtime pinned in go.mod - so every fresh 'make' regenerated uncompilable _templ.go files. Install the go.mod version and reinstall when the binary on PATH reports a different one. * admin: drop stray Makefile change belonging to the templ pin PR Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * notification: join the move destination path in log queue output A NewParentPath of "/" printed //name for moves into the root; path.Join keeps a single leading slash. * log queue: skip a nil event notification instead of logging an empty move Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * pb: tolerate nil chunk entries while scrubbing cipher keys for logs Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * subscription and backup failure logs name the event's file The process/marshal failure logs printed only the event directory and timestamp, so a failed sync left operators guessing which file failed. Use MetadataEventSourceFullPath/MetadataEventTargetFullPath so each failure names the source and target paths without printing the entries. * replication: identify entry-less events without printing the proto Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * s3: scrub cipher keys from the bucket-metadata debug dump buildBucketMetadata marshals the whole filer entry to JSON for a V(3) log line; a chunked entry would emit each chunk's cipher_key base64. Marshal the scrubbed copy instead. --------- Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
157 lines
5.0 KiB
Go
157 lines
5.0 KiB
Go
package filer
|
|
|
|
import (
|
|
"bytes"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/util"
|
|
)
|
|
|
|
// onMetadataChangeEvent is triggered after filer processed change events from local or remote filers
|
|
func (f *Filer) onMetadataChangeEvent(event *filer_pb.SubscribeMetadataResponse) {
|
|
f.maybeReloadFilerConfiguration(event)
|
|
f.maybeReloadRemoteStorageConfigurationAndMapping(event)
|
|
f.onBucketEvents(event)
|
|
f.onEmptyFolderCleanupEvents(event)
|
|
f.onRemoteDeletionEvents(event)
|
|
}
|
|
|
|
func (f *Filer) onBucketEvents(event *filer_pb.SubscribeMetadataResponse) {
|
|
message := event.EventNotification
|
|
oldDir := event.Directory
|
|
newDir := filer_pb.MetadataEventTargetDirectory(event)
|
|
|
|
if filer_pb.IsCreate(event) {
|
|
if newDir == f.DirBucketsPath && message.NewEntry.IsDirectory {
|
|
f.Store.OnBucketCreation(message.NewEntry.Name)
|
|
}
|
|
}
|
|
if filer_pb.IsDelete(event) {
|
|
if oldDir == f.DirBucketsPath && message.OldEntry.IsDirectory {
|
|
f.Store.OnBucketDeletion(message.OldEntry.Name)
|
|
}
|
|
}
|
|
if filer_pb.IsRename(event) {
|
|
if oldDir == f.DirBucketsPath && message.OldEntry.IsDirectory {
|
|
f.Store.OnBucketDeletion(message.OldEntry.Name)
|
|
}
|
|
if newDir == f.DirBucketsPath && message.NewEntry.IsDirectory {
|
|
f.Store.OnBucketCreation(message.NewEntry.Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
// onEmptyFolderCleanupEvents handles create/delete events for empty folder cleanup
|
|
func (f *Filer) onEmptyFolderCleanupEvents(event *filer_pb.SubscribeMetadataResponse) {
|
|
if f.EmptyFolderCleaner == nil || !f.EmptyFolderCleaner.IsEnabled() {
|
|
return
|
|
}
|
|
|
|
message := event.EventNotification
|
|
directory := event.Directory
|
|
eventTime := time.Unix(0, event.TsNs)
|
|
|
|
// Handle delete events - trigger folder cleanup check
|
|
if filer_pb.IsDelete(event) && message.OldEntry != nil {
|
|
f.EmptyFolderCleaner.OnDeleteEvent(directory, message.OldEntry.Name, message.OldEntry.IsDirectory, eventTime)
|
|
f.EmptyFolderCleaner.InvalidateBucketPolicy(directory, message.OldEntry.Name, message.OldEntry.IsDirectory)
|
|
}
|
|
|
|
// Handle create events - cancel pending cleanup for the folder
|
|
if filer_pb.IsCreate(event) && message.NewEntry != nil {
|
|
f.EmptyFolderCleaner.OnCreateEvent(directory, message.NewEntry.Name, message.NewEntry.IsDirectory)
|
|
f.EmptyFolderCleaner.InvalidateBucketPolicy(directory, message.NewEntry.Name, message.NewEntry.IsDirectory)
|
|
}
|
|
|
|
// Handle update events - drop the cached bucket cleanup policy
|
|
if filer_pb.IsUpdate(event) && message.NewEntry != nil {
|
|
f.EmptyFolderCleaner.InvalidateBucketPolicy(directory, message.NewEntry.Name, message.NewEntry.IsDirectory)
|
|
}
|
|
|
|
// Handle rename/move events
|
|
if filer_pb.IsRename(event) {
|
|
// Treat the old location as a delete
|
|
if message.OldEntry != nil {
|
|
f.EmptyFolderCleaner.OnDeleteEvent(directory, message.OldEntry.Name, message.OldEntry.IsDirectory, eventTime)
|
|
f.EmptyFolderCleaner.InvalidateBucketPolicy(directory, message.OldEntry.Name, message.OldEntry.IsDirectory)
|
|
}
|
|
// Treat the new location as a create
|
|
if message.NewEntry != nil {
|
|
newDir := message.NewParentPath
|
|
if newDir == "" {
|
|
newDir = directory
|
|
}
|
|
f.EmptyFolderCleaner.OnCreateEvent(newDir, message.NewEntry.Name, message.NewEntry.IsDirectory)
|
|
f.EmptyFolderCleaner.InvalidateBucketPolicy(newDir, message.NewEntry.Name, message.NewEntry.IsDirectory)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (f *Filer) maybeReloadFilerConfiguration(event *filer_pb.SubscribeMetadataResponse) {
|
|
if !filer_pb.MetadataEventTouchesDirectory(event, DirectoryEtcSeaweedFS) {
|
|
return
|
|
}
|
|
|
|
entry := event.EventNotification.NewEntry
|
|
if entry == nil {
|
|
return
|
|
}
|
|
|
|
glog.V(0).Infof("processing %s ts_ns=%d", event.Directory, event.TsNs)
|
|
if entry.Name == FilerConfName {
|
|
f.reloadFilerConfiguration(entry)
|
|
}
|
|
}
|
|
|
|
func (f *Filer) readEntry(chunks []*filer_pb.FileChunk, size uint64) ([]byte, error) {
|
|
var buf bytes.Buffer
|
|
err := StreamContent(f.MasterClient, &buf, chunks, 0, int64(size))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return buf.Bytes(), nil
|
|
}
|
|
|
|
func (f *Filer) reloadFilerConfiguration(entry *filer_pb.Entry) {
|
|
fc := NewFilerConf()
|
|
err := fc.loadFromChunks(f, entry.Content, entry.GetChunks(), FileSize(entry))
|
|
if err != nil {
|
|
glog.Errorf("read filer conf chunks: %v", err)
|
|
return
|
|
}
|
|
f.FilerConf = fc
|
|
}
|
|
|
|
func (f *Filer) LoadFilerConf() {
|
|
fc := NewFilerConf()
|
|
err := util.Retry("loadFilerConf", func() error {
|
|
return fc.loadFromFiler(f)
|
|
})
|
|
if err != nil {
|
|
glog.Errorf("read filer conf: %v", err)
|
|
return
|
|
}
|
|
f.FilerConf = fc
|
|
}
|
|
|
|
// //////////////////////////////////
|
|
// load and maintain remote storages
|
|
// //////////////////////////////////
|
|
func (f *Filer) LoadRemoteStorageConfAndMapping() {
|
|
if err := f.RemoteStorage.LoadRemoteStorageConfigurationsAndMapping(f); err != nil {
|
|
glog.Errorf("read remote conf and mapping: %v", err)
|
|
return
|
|
}
|
|
}
|
|
func (f *Filer) maybeReloadRemoteStorageConfigurationAndMapping(event *filer_pb.SubscribeMetadataResponse) {
|
|
if !filer_pb.MetadataEventTouchesDirectory(event, DirectoryEtcRemote) {
|
|
return
|
|
}
|
|
|
|
// a mount left behind after remote.unmount would still send deletes to a
|
|
// remote the filer no longer owns
|
|
f.LoadRemoteStorageConfAndMapping()
|
|
}
|