diff --git a/weed/credential/filer_etc/filer_etc_identity.go b/weed/credential/filer_etc/filer_etc_identity.go index 24b594e801..f4702b034c 100644 --- a/weed/credential/filer_etc/filer_etc_identity.go +++ b/weed/credential/filer_etc/filer_etc_identity.go @@ -39,6 +39,17 @@ func (store *FilerEtcStore) LoadConfiguration(ctx context.Context) (*iam_pb.S3Ap } } + fromLegacy := map[any]bool{} + for _, identity := range s3cfg.Identities { + fromLegacy[identity] = true + } + for _, sa := range s3cfg.ServiceAccounts { + fromLegacy[sa] = true + } + for _, g := range s3cfg.Groups { + fromLegacy[g] = true + } + // 2. Load from multi-file structure (high priority, overrides legacy details) if _, err := store.loadFromMultiFile(ctx, s3cfg); err != nil { return s3cfg, err @@ -58,7 +69,23 @@ func (store *FilerEtcStore) LoadConfiguration(ctx context.Context) (*iam_pb.S3Ap // This ensures that all identities (including legacy ones) are written to individual files // and the legacy file is renamed. if foundLegacy { - if err := store.migrateToMultiFile(ctx, content); err != nil { + loaded := map[string]bool{} + for _, identity := range s3cfg.Identities { + if !fromLegacy[identity] { + loaded[IamIdentitiesDirectory+"/"+identity.Name] = true + } + } + for _, sa := range s3cfg.ServiceAccounts { + if !fromLegacy[sa] { + loaded[IamServiceAccountsDirectory+"/"+sa.Id] = true + } + } + for _, g := range s3cfg.Groups { + if !fromLegacy[g] { + loaded[IamGroupsDirectory+"/"+g.Name] = true + } + } + if err := store.migrateToMultiFile(ctx, content, loaded); err != nil { glog.Errorf("Failed to migrate IAM configuration to multi-file layout: %v", err) return s3cfg, nil } @@ -141,7 +168,7 @@ func (store *FilerEtcStore) loadFromMultiFile(ctx context.Context, s3cfg *iam_pb return hasIdentities, nil } -func (store *FilerEtcStore) migrateToMultiFile(ctx context.Context, legacyContent []byte) error { +func (store *FilerEtcStore) migrateToMultiFile(ctx context.Context, legacyContent []byte, loaded map[string]bool) error { current, found, err := store.readInsideFiler(ctx, filer.IamConfigDirectory, IamLegacyIdentityFile) if err != nil { return err @@ -157,10 +184,16 @@ func (store *FilerEtcStore) migrateToMultiFile(ctx context.Context, legacyConten glog.Infof("Migrating IAM configuration to multi-file layout...") - // An existing per-entry file is newer than the legacy copy, and may have - // been rewritten or removed after this load read it: never overwrite it. + // A per-entry file is newer than the legacy copy, and may have been + // rewritten or removed after this load read it: never write it back. + createIfAbsent := func(subDir, name string, v any) error { + if loaded[subDir+"/"+name] { + return nil + } + return store.createIfAbsent(ctx, subDir, name, v) + } for _, identity := range legacy.Identities { - if err := store.createIfAbsent(ctx, IamIdentitiesDirectory, identity.Name, identity); err != nil { + if err := createIfAbsent(IamIdentitiesDirectory, identity.Name, identity); err != nil { return err } } @@ -168,7 +201,7 @@ func (store *FilerEtcStore) migrateToMultiFile(ctx context.Context, legacyConten if err := validateServiceAccountId(sa.Id); err != nil { return err } - if err := store.createIfAbsent(ctx, IamServiceAccountsDirectory, sa.Id, sa); err != nil { + if err := createIfAbsent(IamServiceAccountsDirectory, sa.Id, sa); err != nil { return err } } @@ -177,7 +210,7 @@ func (store *FilerEtcStore) migrateToMultiFile(ctx context.Context, legacyConten if g.Name == "" { return fmt.Errorf("group name is required") } - if err := store.createIfAbsent(ctx, IamGroupsDirectory, g.Name, g); err != nil { + if err := createIfAbsent(IamGroupsDirectory, g.Name, g); err != nil { return err } }