credential: keep a per-entry file deleted after the IAM load deleted

An entry the load read from its own file is already migrated. When that
file is removed before the migration runs, the legacy copy must not
recreate it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chris LuandClaude Opus 5.5 committed 2026-11-13 03:10:41 -08:00
1 parent 7bbefe6a78
commit 768f40299e
1 file changed
+40 -7
@@ -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
}
}