diff --git a/components/engine/plugin/backend_linux.go b/components/engine/plugin/backend_linux.go index d40dd83e06..ae6463972c 100644 --- a/components/engine/plugin/backend_linux.go +++ b/components/engine/plugin/backend_linux.go @@ -200,15 +200,29 @@ func (pm *Manager) Set(name string, args []string) error { // CreateFromContext creates a plugin from the given pluginDir which contains // both the rootfs and the config.json and a repoName with optional tag. func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.Reader, options *types.PluginCreateOptions) error { + repoName := options.RepoName + ref, err := distribution.GetRef(repoName) + if err != nil { + return err + } + + name := ref.Name() + tag := distribution.GetTag(ref) pluginID := stringid.GenerateNonCryptoID() + p := v2.NewPlugin(name, pluginID, pm.runRoot, pm.libRoot, tag) + + if v, _ := pm.pluginStore.GetByName(p.Name()); v != nil { + return fmt.Errorf("plugin %q already exists", p.Name()) + } + pluginDir := filepath.Join(pm.libRoot, pluginID) if err := os.MkdirAll(pluginDir, 0755); err != nil { return err } // In case an error happens, remove the created directory. - if err := pm.createFromContext(ctx, pluginID, pluginDir, tarCtx, options); err != nil { + if err := pm.createFromContext(ctx, tarCtx, pluginDir, repoName, p); err != nil { if err := os.RemoveAll(pluginDir); err != nil { logrus.Warnf("unable to remove %q from failed plugin creation: %v", pluginDir, err) } @@ -218,20 +232,11 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.Reader, opti return nil } -func (pm *Manager) createFromContext(ctx context.Context, pluginID, pluginDir string, tarCtx io.Reader, options *types.PluginCreateOptions) error { +func (pm *Manager) createFromContext(ctx context.Context, tarCtx io.Reader, pluginDir, repoName string, p *v2.Plugin) error { if err := chrootarchive.Untar(tarCtx, pluginDir, nil); err != nil { return err } - repoName := options.RepoName - ref, err := distribution.GetRef(repoName) - if err != nil { - return err - } - name := ref.Name() - tag := distribution.GetTag(ref) - - p := v2.NewPlugin(name, pluginID, pm.runRoot, pm.libRoot, tag) if err := p.InitPlugin(); err != nil { return err } diff --git a/components/engine/plugin/store/store.go b/components/engine/plugin/store/store.go index fe834dc4d7..3b4260156d 100644 --- a/components/engine/plugin/store/store.go +++ b/components/engine/plugin/store/store.go @@ -106,6 +106,13 @@ func (ps *Store) Add(p *v2.Plugin) error { if v, exist := ps.plugins[p.GetID()]; exist { return fmt.Errorf("plugin %q has the same ID %s as %q", p.Name(), p.GetID(), v.Name()) } + // Since both Pull() and CreateFromContext() calls GetByName() before any plugin + // to search for collision (to fail fast), it is unlikely the following check + // will return an error. + // However, in case two CreateFromContext() are called at the same time, + // there is still a remote possibility that a collision might happen. + // For that reason we still perform the collision check below as it is protected + // by ps.Lock() and ps.Unlock() above. if _, exist := ps.nameToID[p.Name()]; exist { return fmt.Errorf("plugin %q already exists", p.Name()) }