Refactor Admin UI to use unified IAM storage and add MultipleFileStore (#8101)

* Refactor Admin UI to use unified IAM storage and add MultipleFileStore

* Address PR feedback: fix renames, error handling, and sync logic in FilerMultipleStore

* Address refined PR feedback: safe rename order, rollback logic, and structural sync refinement

* Optimize LoadConfiguration: use streaming callback for memory efficiency

* Refactor UpdateUser: log rollback failures during rename

* Implement PolicyManager for FilerMultipleStore

* include the filer_multiple backend configuration

* Implement cross-S3 synchronization and proper shutdown for all IAM backends

* Extract Admin UI refactoring to a separate PR
This commit is contained in:
Chris Lu
2026-01-23 20:12:59 -08:00
committed by GitHub
parent 535be3096b
commit f6318edbc9
9 changed files with 586 additions and 27 deletions

View File

@@ -61,6 +61,9 @@ type IdentityAccessManagement struct {
// Bucket policy engine for evaluating bucket policies
policyEngine *BucketPolicyEngine
// background polling
stopChan chan struct{}
// useStaticConfig indicates if the configuration was loaded from a static file
useStaticConfig bool
@@ -161,8 +164,8 @@ func NewIdentityAccessManagementWithStore(option *S3ApiServerOption, explicitSto
}
iam.credentialManager = credentialManager
iam.stopChan = make(chan struct{})
// First, try to load configurations from file or filer
// First, try to load configurations from file or filer
startConfigFile := option.Config
if startConfigFile == "" {
@@ -186,18 +189,22 @@ func NewIdentityAccessManagementWithStore(option *S3ApiServerOption, explicitSto
iam.m.Unlock()
}
// Always try to load/merge config from credential manager (filer)
// This ensures we get both static users (from file) and dynamic users (from filer)
// Always try to load/merge config from credential manager (filer/db)
// This ensures we get both static users (from file) and dynamic users (from backend)
glog.V(3).Infof("loading dynamic config from credential manager")
if err := iam.loadS3ApiConfigurationFromFiler(option); err != nil {
glog.Warningf("fail to load config: %v", err)
}
// Only consider config loaded if we actually have identities
// Don't block environment variable fallback just because filer call succeeded
// iam.m.RLock()
// configLoaded = len(iam.identities) > 0
// iam.m.RUnlock()
// Determine whether to start background polling for updates
// We poll if using a store that doesn't support real-time events (like Postgres)
if store := iam.credentialManager.GetStore(); store != nil {
storeName := store.GetName()
if storeName == credential.StoreTypePostgres {
glog.V(1).Infof("Starting background IAM polling for store: %s", storeName)
go iam.pollIamConfigChanges(1 * time.Minute)
}
}
// Check for AWS environment variables and merge them if present
// This serves as an in-memory "static" configuration
@@ -227,6 +234,31 @@ func NewIdentityAccessManagementWithStore(option *S3ApiServerOption, explicitSto
return iam
}
func (iam *IdentityAccessManagement) pollIamConfigChanges(interval time.Duration) {
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if err := iam.LoadS3ApiConfigurationFromCredentialManager(); err != nil {
glog.Warningf("failed to reload IAM configuration via polling: %v", err)
}
case <-iam.stopChan:
return
}
}
}
func (iam *IdentityAccessManagement) Shutdown() {
if iam.stopChan != nil {
close(iam.stopChan)
}
if iam.credentialManager != nil {
iam.credentialManager.Shutdown()
}
}
// loadEnvironmentVariableCredentials loads AWS credentials from environment variables
// and adds them as a static admin identity. This function is idempotent and can be
// called multiple times (e.g., after configuration reloads).