diff --git a/server/compose/service/namespace.go b/server/compose/service/namespace.go index b141c4d51..a30d03c15 100644 --- a/server/compose/service/namespace.go +++ b/server/compose/service/namespace.go @@ -278,59 +278,62 @@ func (svc namespace) Clone(ctx context.Context, namespaceID uint64, dup *types.N ) err = func() error { - // Preparation - // - target namespace - targetNs, err := loadNamespace(ctx, svc.store, namespaceID) - if errors.IsNotFound(err) { - return NamespaceErrNotFound() - } else if err != nil { - return err - } - aProps.setNamespace(targetNs) - - // - destination namespace - if dup.Slug != "" { - dstNs, err := store.LookupComposeNamespaceBySlug(ctx, svc.store, dup.Slug) - if err != nil && err != store.ErrNotFound { + err = store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) error { + // Preparation + // - target namespace + targetNs, err := loadNamespace(ctx, s, namespaceID) + if errors.IsNotFound(err) { + return NamespaceErrNotFound() + } else if err != nil { return err } - if dstNs != nil { - return NamespaceErrHandleNotUnique() + aProps.setNamespace(targetNs) + + // - destination namespace + if dup.Slug != "" { + dstNs, err := store.LookupComposeNamespaceBySlug(ctx, s, dup.Slug) + if err != nil && err != store.ErrNotFound { + return err + } + if dstNs != nil { + return NamespaceErrHandleNotUnique() + } } - } - // Access control - if err = svc.canExport(ctx, targetNs); err != nil { - return err - } + // Access control + if err = svc.canExport(ctx, targetNs); err != nil { + return err + } - // get namespace resources - nn, err := decoder() - if err != nil { - return err - } + // get namespace resources + nn, err := decoder() + if err != nil { + return err + } - aProps.setNamespace(dup) - dup, err = svc.envoyRun(ctx, nn, targetNs, dup) + aProps.setNamespace(dup) + dup, err = svc.envoyRun(ctx, s, nn, targetNs, dup) + if err != nil { + return err + } + + dup, err = store.LookupComposeNamespaceBySlug(ctx, s, dup.Slug) + if err != nil { + return err + } + tag := locale.GetAcceptLanguageFromContext(ctx) + dup.DecodeTranslations(svc.locale.Locale().ResourceTranslations(tag, dup.ResourceTranslation())) + + aProps.setNamespace(dup) + + return nil + }) if err != nil { return err } err = svc.reloadServices(ctx, dup) - if err != nil { - return err - } - - dup, err = store.LookupComposeNamespaceBySlug(ctx, svc.store, dup.Slug) - if err != nil { - return err - } - tag := locale.GetAcceptLanguageFromContext(ctx) - dup.DecodeTranslations(svc.locale.Locale().ResourceTranslations(tag, dup.ResourceTranslation())) - - aProps.setNamespace(dup) - - return nil + return err }() return dup, svc.recordAction(ctx, aProps, NamespaceActionClone, err) @@ -437,54 +440,60 @@ func (svc namespace) ImportRun(ctx context.Context, sessionID uint64, dup *types aProps = &namespaceActionProps{namespace: dup} ) - err = func() (err error) { - // access control - if err = svc.canImport(ctx); err != nil { - return err - } + err = func() error { + var ( + newNS *types.Namespace + ) - if !handle.IsValid(dup.Slug) { - return NamespaceErrInvalidHandle() - } - - if dup.Slug != "" { - // check for duplicate - dstNs, err := store.LookupComposeNamespaceBySlug(ctx, svc.store, dup.Slug) - if err != nil && err != store.ErrNotFound { + err = store.Tx(ctx, svc.store, func(ctx context.Context, s store.Storer) error { + // access control + if err = svc.canImport(ctx); err != nil { return err } - if dstNs != nil { - return NamespaceErrHandleNotUnique() + + if !handle.IsValid(dup.Slug) { + return NamespaceErrInvalidHandle() } - } - // session - var ( - session namespaceImportSession - ok bool - newNS *types.Namespace - ) - if session, ok = namespaceSessionStore[sessionID]; !ok { - return NamespaceErrImportSessionNotFound() - } - defer func() { - delete(namespaceSessionStore, sessionID) - }() + if dup.Slug != "" { + // check for duplicate + dstNs, err := store.LookupComposeNamespaceBySlug(ctx, svc.store, dup.Slug) + if err != nil && err != store.ErrNotFound { + return err + } + if dstNs != nil { + return NamespaceErrHandleNotUnique() + } + } - aProps.setNamespace(dup) + // session + var ( + session namespaceImportSession + ok bool + ) + if session, ok = namespaceSessionStore[sessionID]; !ok { + return NamespaceErrImportSessionNotFound() + } + defer func() { + delete(namespaceSessionStore, sessionID) + }() - newNS, err = svc.envoyRun(ctx, session.Nodes, &types.Namespace{ID: session.NamespaceID, Slug: session.Slug, Name: session.Name}, dup) + aProps.setNamespace(dup) + + newNS, err = svc.envoyRun(ctx, s, session.Nodes, &types.Namespace{ID: session.NamespaceID, Slug: session.Slug, Name: session.Name}, dup) + if err != nil { + return err + } + + aProps.setNamespace(newNS) + return nil + }) if err != nil { return err } err = svc.reloadServices(ctx, newNS) - if err != nil { - return err - } - - aProps.setNamespace(newNS) - return nil + return err }() return dup, svc.recordAction(ctx, aProps, NamespaceActionImportRun, err) @@ -739,7 +748,7 @@ func (svc namespace) canImport(ctx context.Context) error { return nil } -func (svc namespace) envoyRun(ctx context.Context, nodes envoyx.NodeSet, oldNS, newNS *types.Namespace) (ns *types.Namespace, err error) { +func (svc namespace) envoyRun(ctx context.Context, s store.Storer, nodes envoyx.NodeSet, oldNS, newNS *types.Namespace) (ns *types.Namespace, err error) { // Get the NS node oldRef := envoyx.Ref{ ResourceType: types.NamespaceResourceType, @@ -787,7 +796,7 @@ func (svc namespace) envoyRun(ctx context.Context, nodes envoyx.NodeSet, oldNS, roles, _, err := svc.envoy.Decode(ctx, envoyx.DecodeParams{ Type: envoyx.DecodeTypeStore, Params: map[string]any{ - "storer": svc.store, + "storer": s, "dal": dal.Service(), }, Filter: map[string]envoyx.ResourceFilter{ @@ -806,7 +815,7 @@ func (svc namespace) envoyRun(ctx context.Context, nodes envoyx.NodeSet, oldNS, gg, err := svc.envoy.Bake(ctx, envoyx.EncodeParams{ Type: envoyx.EncodeTypeStore, Params: map[string]any{ - "storer": svc.store, + "storer": s, "dal": dal.Service(), }, }, nil, nodes...) @@ -817,7 +826,7 @@ func (svc namespace) envoyRun(ctx context.Context, nodes envoyx.NodeSet, oldNS, err = svc.envoy.Encode(ctx, envoyx.EncodeParams{ Type: envoyx.EncodeTypeStore, Params: map[string]any{ - "storer": svc.store, + "storer": s, "dal": dal.Service(), }, }, gg)