mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-16 01:44:03 +03:00
9335e40d76
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7068 GitOrigin-RevId: 37735c0020df23857015871890d2f6774166d7b1
152 lines
4.1 KiB
Go
152 lines
4.1 KiB
Go
package plugins
|
|
|
|
/*
|
|
some of the code here is borrowed from the krew codebse (kubernetes)
|
|
and the copyright belongs to the respective authors.
|
|
|
|
source: https://github.com/kubernetes-sigs/krew/tree/master/internal
|
|
*/
|
|
|
|
import (
|
|
stderrors "errors"
|
|
"fmt"
|
|
"io/fs"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/goccy/go-yaml"
|
|
"github.com/hasura/graphql-engine/cli/v2/internal/errors"
|
|
"github.com/hasura/graphql-engine/cli/v2/plugins/paths"
|
|
"github.com/spf13/afero"
|
|
)
|
|
|
|
func (c *Config) findPluginManifestFiles(indexDir string) ([]string, error) {
|
|
var op errors.Op = "plugins.Config.findPluginManifestFiles"
|
|
c.Logger.Debugf("finding plugin manifest files in directory %v", indexDir)
|
|
var out []string
|
|
fs := afero.Afero{
|
|
Fs: afero.NewOsFs(),
|
|
}
|
|
err := fs.Walk(indexDir, func(path string, info os.FileInfo, err error) error {
|
|
if info == nil {
|
|
if err != nil {
|
|
return errors.E(op, err)
|
|
}
|
|
return nil
|
|
}
|
|
if info.Mode().IsRegular() && filepath.Ext(info.Name()) == paths.ManifestExtension {
|
|
out = append(out, path)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, errors.E(op, err)
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
// LoadPluginListFromFS will parse and retrieve all plugin files.
|
|
func (c *Config) LoadPluginListFromFS(indexDir string) (Plugins, error) {
|
|
var op errors.Op = "plugins.Config.LoadPluginListFromFS"
|
|
indexDir, err := filepath.EvalSymlinks(indexDir)
|
|
if err != nil {
|
|
return nil, errors.E(op, err)
|
|
}
|
|
|
|
files, err := c.findPluginManifestFiles(indexDir)
|
|
if err != nil {
|
|
return nil, errors.E(op, fmt.Errorf("failed to scan plugins in index directory: %w", err))
|
|
}
|
|
|
|
return c.LoadPlugins(files), nil
|
|
}
|
|
|
|
// LoadPluginByName loads a plugins index file by its name. When plugin
|
|
// file not found, it returns an error that can be checked with stderrors.Is(err, fs.ErrNotExist)
|
|
func (c *Config) LoadPluginByName(pluginName string) (*PluginVersions, error) {
|
|
var op errors.Op = "plugins.Config.LoadPluginByName"
|
|
c.Logger.Debugf("loading plugin %s", pluginName)
|
|
if !IsSafePluginName(pluginName) {
|
|
return nil, errors.E(op, fmt.Errorf("plugin name %q not allowed", pluginName))
|
|
}
|
|
|
|
files, err := c.findPluginManifestFiles(c.Paths.IndexPluginsPath())
|
|
if err != nil {
|
|
return nil, errors.E(op, fmt.Errorf("failed to scan plugins in index directory: %w", err))
|
|
}
|
|
|
|
ps := c.LoadPlugins(files, pluginName)
|
|
if _, ok := ps[pluginName]; !ok {
|
|
return nil, errors.E(op, os.ErrNotExist)
|
|
}
|
|
|
|
return ps[pluginName], nil
|
|
}
|
|
|
|
func (c *Config) LoadPlugins(files []string, pluginName ...string) Plugins {
|
|
c.Logger.Debugf("loading plugins")
|
|
ps := Plugins{}
|
|
for _, file := range files {
|
|
p, err := c.ReadPluginFromFile(file)
|
|
if err != nil {
|
|
c.Logger.Debugf("failed to read or parse plugin manifest: %v", err)
|
|
continue
|
|
}
|
|
if p.ParsedVersion == nil {
|
|
c.Logger.Debugf("version of plugin %s cannot be nil", p.Name)
|
|
continue
|
|
}
|
|
if len(pluginName) != 0 {
|
|
var isFound bool
|
|
for _, name := range pluginName {
|
|
if name == p.Name {
|
|
isFound = true
|
|
break
|
|
}
|
|
}
|
|
if !isFound {
|
|
continue
|
|
}
|
|
}
|
|
if _, ok := ps[p.Name]; !ok {
|
|
ps[p.Name] = NewPluginVersions()
|
|
}
|
|
err = ps[p.Name].Append(p)
|
|
if err != nil {
|
|
c.Logger.Debugf("failed to append version %s for plugin %s: %v", p.Version, p.Name, err)
|
|
continue
|
|
}
|
|
}
|
|
return ps
|
|
}
|
|
|
|
// ReadPluginFromFile loads a file from the FS. When plugin file not found, it
|
|
// returns an error that can be checked with stderrors.Is(err, fs.ErrNotExist).
|
|
func (c *Config) ReadPluginFromFile(path string) (Plugin, error) {
|
|
var op errors.Op = "plugins.Config.ReadPluginFromFile"
|
|
f, err := os.Open(path)
|
|
if stderrors.Is(err, fs.ErrNotExist) {
|
|
return Plugin{}, errors.E(op, err)
|
|
} else if err != nil {
|
|
return Plugin{}, errors.E(op, fmt.Errorf("failed to open index file: %w", err))
|
|
}
|
|
defer f.Close()
|
|
var plugin Plugin
|
|
b, err := ioutil.ReadAll(f)
|
|
if err != nil {
|
|
return plugin, errors.E(op, err)
|
|
}
|
|
err = yaml.Unmarshal(b, &plugin)
|
|
if err != nil {
|
|
return plugin, errors.E(op, fmt.Errorf("failed to decode plugin manifest: %w", err))
|
|
}
|
|
plugin.ParseVersion()
|
|
err = plugin.ValidatePlugin(plugin.Name)
|
|
if err != nil {
|
|
return plugin, errors.E(op, fmt.Errorf("plugin manifest validation error: %w", err))
|
|
}
|
|
return plugin, nil
|
|
}
|