mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Plugins: Remove dead CLI code and use pkg/plugins for uninstall process (#67711)
* remove dead code and use pkg/plugins for uninstall process * fix linter
This commit is contained in:
parent
471a03328b
commit
6cd042ed16
@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
|
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RunCLI is the entrypoint for the grafana-cli command. It returns the exit code for the grafana-cli program.
|
// CLICommand is the entrypoint for the grafana-cli command. It returns the exit code for the grafana-cli program.
|
||||||
func CLICommand(version string) *cli.Command {
|
func CLICommand(version string) *cli.Command {
|
||||||
return &cli.Command{
|
return &cli.Command{
|
||||||
Name: "cli",
|
Name: "cli",
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -13,6 +14,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
|
"github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
|
||||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
|
"github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
|
||||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
|
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||||
"github.com/grafana/grafana/pkg/plugins/storage"
|
"github.com/grafana/grafana/pkg/plugins/storage"
|
||||||
)
|
)
|
||||||
@ -104,6 +106,21 @@ func installPlugin(ctx context.Context, pluginID, version string, c utils.Comman
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// uninstallPlugin removes the plugin directory
|
||||||
|
func uninstallPlugin(_ context.Context, pluginID string, c utils.CommandLine) error {
|
||||||
|
logger.Infof("Removing plugin: %v\n", pluginID)
|
||||||
|
|
||||||
|
pluginPath := filepath.Join(c.PluginDirectory(), pluginID)
|
||||||
|
fs := plugins.NewLocalFS(pluginPath)
|
||||||
|
|
||||||
|
logger.Debugf("Removing directory %v\n", pluginPath)
|
||||||
|
err := fs.Remove()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func osAndArchString() string {
|
func osAndArchString() string {
|
||||||
osString := strings.ToLower(runtime.GOOS)
|
osString := strings.ToLower(runtime.GOOS)
|
||||||
arch := runtime.GOARCH
|
arch := runtime.GOARCH
|
||||||
|
@ -1,31 +1,25 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
|
|
||||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
|
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var removePlugin func(pluginPath, id string) error = services.RemoveInstalledPlugin
|
|
||||||
|
|
||||||
func (cmd Command) removeCommand(c utils.CommandLine) error {
|
func (cmd Command) removeCommand(c utils.CommandLine) error {
|
||||||
pluginPath := c.PluginDirectory()
|
pluginID := c.Args().First()
|
||||||
|
if pluginID == "" {
|
||||||
plugin := c.Args().First()
|
|
||||||
if plugin == "" {
|
|
||||||
return errors.New("missing plugin parameter")
|
return errors.New("missing plugin parameter")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := removePlugin(pluginPath, plugin)
|
err := uninstallPlugin(context.Background(), pluginID, c)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "no such file or directory") {
|
if strings.Contains(err.Error(), "no such file or directory") {
|
||||||
return fmt.Errorf("plugin does not exist")
|
return fmt.Errorf("plugin does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
logRestartNotice()
|
logRestartNotice()
|
||||||
|
@ -49,15 +49,16 @@ func (cmd Command) upgradeAllCommand(c utils.CommandLine) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
for _, p := range pluginsToUpgrade {
|
for _, p := range pluginsToUpgrade {
|
||||||
logger.Infof("Updating %v \n", p.ID)
|
logger.Infof("Updating %v \n", p.ID)
|
||||||
|
|
||||||
err := services.RemoveInstalledPlugin(pluginsDir, p.ID)
|
err = uninstallPlugin(ctx, p.ID, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = installPlugin(context.Background(), p.ID, "", c)
|
err = installPlugin(ctx, p.ID, "", c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -12,32 +12,32 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (cmd Command) upgradeCommand(c utils.CommandLine) error {
|
func (cmd Command) upgradeCommand(c utils.CommandLine) error {
|
||||||
|
ctx := context.Background()
|
||||||
pluginsDir := c.PluginDirectory()
|
pluginsDir := c.PluginDirectory()
|
||||||
pluginName := c.Args().First()
|
pluginID := c.Args().First()
|
||||||
|
|
||||||
localPlugin, err := services.ReadPlugin(pluginsDir, pluginName)
|
|
||||||
|
|
||||||
|
localPlugin, err := services.ReadPlugin(pluginsDir, pluginID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
plugin, err2 := cmd.Client.GetPlugin(pluginName, c.PluginRepoURL())
|
plugin, err := cmd.Client.GetPlugin(pluginID, c.PluginRepoURL())
|
||||||
if err2 != nil {
|
if err != nil {
|
||||||
return err2
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if shouldUpgrade(localPlugin.Info.Version, &plugin) {
|
if shouldUpgrade(localPlugin.Info.Version, &plugin) {
|
||||||
if err := services.RemoveInstalledPlugin(pluginsDir, pluginName); err != nil {
|
if err = uninstallPlugin(ctx, pluginID, c); err != nil {
|
||||||
return fmt.Errorf("failed to remove plugin '%s': %w", pluginName, err)
|
return fmt.Errorf("failed to remove plugin '%s': %w", pluginID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := installPlugin(context.Background(), pluginName, "", c)
|
err = installPlugin(ctx, pluginID, "", c)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
logRestartNotice()
|
logRestartNotice()
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Infof("%s %s is up to date \n", color.GreenString("✔"), pluginName)
|
logger.Infof("%s %s is up to date \n", color.GreenString("✔"), pluginID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
@ -17,9 +14,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
|
"github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GrafanaComClient struct {
|
type GrafanaComClient struct{}
|
||||||
retryCount int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *GrafanaComClient) GetPlugin(pluginId, repoUrl string) (models.Plugin, error) {
|
func (client *GrafanaComClient) GetPlugin(pluginId, repoUrl string) (models.Plugin, error) {
|
||||||
logger.Debugf("getting plugin metadata from: %v pluginId: %v \n", repoUrl, pluginId)
|
logger.Debugf("getting plugin metadata from: %v pluginId: %v \n", repoUrl, pluginId)
|
||||||
@ -42,77 +37,6 @@ func (client *GrafanaComClient) GetPlugin(pluginId, repoUrl string) (models.Plug
|
|||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *GrafanaComClient) DownloadFile(pluginName string, tmpFile *os.File, url string, checksum string) (err error) {
|
|
||||||
// Try handling URL as a local file path first
|
|
||||||
if _, err := os.Stat(url); err == nil {
|
|
||||||
// We can ignore this gosec G304 warning since `url` stems from command line flag "pluginUrl". If the
|
|
||||||
// user shouldn't be able to read the file, it should be handled through filesystem permissions.
|
|
||||||
// nolint:gosec
|
|
||||||
f, err := os.Open(url)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%v: %w", "Failed to read plugin archive", err)
|
|
||||||
}
|
|
||||||
_, err = io.Copy(tmpFile, f)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%v: %w", "Failed to copy plugin archive", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
client.retryCount = 0
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
client.retryCount++
|
|
||||||
if client.retryCount < 3 {
|
|
||||||
logger.Info("Failed downloading. Will retry once.")
|
|
||||||
err = tmpFile.Truncate(0)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err = tmpFile.Seek(0, 0)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = client.DownloadFile(pluginName, tmpFile, url, checksum)
|
|
||||||
} else {
|
|
||||||
client.retryCount = 0
|
|
||||||
failure := fmt.Sprintf("%v", r)
|
|
||||||
if failure == "runtime error: makeslice: len out of range" {
|
|
||||||
err = fmt.Errorf("corrupt HTTP response from source, please try again")
|
|
||||||
} else {
|
|
||||||
panic(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Using no timeout here as some plugins can be bigger and smaller timeout would prevent to download a plugin on
|
|
||||||
// slow network. As this is CLI operation hanging is not a big of an issue as user can just abort.
|
|
||||||
bodyReader, err := sendRequest(HttpClientNoTimeout, url)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%v: %w", "Failed to send request", err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := bodyReader.Close(); err != nil {
|
|
||||||
logger.Warn("Failed to close body", "err", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
w := bufio.NewWriter(tmpFile)
|
|
||||||
h := sha256.New()
|
|
||||||
if _, err = io.Copy(w, io.TeeReader(bodyReader, h)); err != nil {
|
|
||||||
return fmt.Errorf("%v: %w", "failed to compute SHA256 checksum", err)
|
|
||||||
}
|
|
||||||
if err := w.Flush(); err != nil {
|
|
||||||
return fmt.Errorf("failed to write to %q: %w", tmpFile.Name(), err)
|
|
||||||
}
|
|
||||||
if len(checksum) > 0 && checksum != fmt.Sprintf("%x", h.Sum(nil)) {
|
|
||||||
return fmt.Errorf("expected SHA256 checksum does not match the downloaded archive - please contact security@grafana.com")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *GrafanaComClient) ListAllPlugins(repoUrl string) (models.PluginRepo, error) {
|
func (client *GrafanaComClient) ListAllPlugins(repoUrl string) (models.PluginRepo, error) {
|
||||||
body, err := sendRequestGetBytes(HttpClient, repoUrl, "repo")
|
body, err := sendRequestGetBytes(HttpClient, repoUrl, "repo")
|
||||||
|
|
||||||
|
@ -15,12 +15,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
IoHelper models.IoUtil = IoUtilImp{}
|
IoHelper models.IoUtil = IoUtilImp{}
|
||||||
HttpClient http.Client
|
HttpClient http.Client
|
||||||
HttpClientNoTimeout http.Client
|
GrafanaVersion string
|
||||||
GrafanaVersion string
|
ErrNotFoundError = errors.New("404 not found error")
|
||||||
ErrNotFoundError = errors.New("404 not found error")
|
Logger *logger.CLILogger
|
||||||
Logger *logger.CLILogger
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type BadRequestError struct {
|
type BadRequestError struct {
|
||||||
@ -37,9 +36,7 @@ func (e *BadRequestError) Error() string {
|
|||||||
|
|
||||||
func Init(version string, skipTLSVerify bool, debugMode bool) {
|
func Init(version string, skipTLSVerify bool, debugMode bool) {
|
||||||
GrafanaVersion = version
|
GrafanaVersion = version
|
||||||
|
|
||||||
HttpClient = makeHttpClient(skipTLSVerify, 10*time.Second)
|
HttpClient = makeHttpClient(skipTLSVerify, 10*time.Second)
|
||||||
HttpClientNoTimeout = makeHttpClient(skipTLSVerify, 0)
|
|
||||||
Logger = logger.New(debugMode)
|
Logger = logger.New(debugMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +70,7 @@ func ReadPlugin(pluginDir, pluginName string) (models.InstalledPlugin, error) {
|
|||||||
pluginDataPath := filepath.Join(pluginDir, pluginName, "plugin.json")
|
pluginDataPath := filepath.Join(pluginDir, pluginName, "plugin.json")
|
||||||
data, err = IoHelper.ReadFile(pluginDataPath)
|
data, err = IoHelper.ReadFile(pluginDataPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.InstalledPlugin{}, errors.New("Could not find dist/plugin.json or plugin.json on " + pluginName + " in " + pluginDir)
|
return models.InstalledPlugin{}, errors.New("Could not find dist/plugin.json or plugin.json for " + pluginName + " in " + pluginDir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,16 +102,3 @@ func GetLocalPlugins(pluginDir string) []models.InstalledPlugin {
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func RemoveInstalledPlugin(pluginPath, pluginName string) error {
|
|
||||||
logger.Infof("Removing plugin: %v\n", pluginName)
|
|
||||||
pluginDir := filepath.Join(pluginPath, pluginName)
|
|
||||||
|
|
||||||
_, err := IoHelper.Stat(pluginDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Debugf("Removing directory %v\n", pluginDir)
|
|
||||||
return IoHelper.RemoveAll(pluginDir)
|
|
||||||
}
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
|
"github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
|
||||||
@ -27,7 +25,6 @@ type CommandLine interface {
|
|||||||
|
|
||||||
type ApiClient interface {
|
type ApiClient interface {
|
||||||
GetPlugin(pluginId, repoUrl string) (models.Plugin, error)
|
GetPlugin(pluginId, repoUrl string) (models.Plugin, error)
|
||||||
DownloadFile(pluginName string, tmpFile *os.File, url string, checksum string) (err error)
|
|
||||||
ListAllPlugins(repoUrl string) (models.PluginRepo, error)
|
ListAllPlugins(repoUrl string) (models.PluginRepo, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user