mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
grafana-cli: Upgrade to urfave/cli v2 (#22402)
* grafana-cli: Upgrade to urfave/cli v2
This commit is contained in:
@@ -1,34 +1,33 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/fatih/color"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/commands/datamigrations"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func runDbCommand(command func(commandLine utils.CommandLine, sqlStore *sqlstore.SqlStore) error) func(context *cli.Context) {
|
||||
return func(context *cli.Context) {
|
||||
func runDbCommand(command func(commandLine utils.CommandLine, sqlStore *sqlstore.SqlStore) error) func(context *cli.Context) error {
|
||||
return func(context *cli.Context) error {
|
||||
cmd := &utils.ContextCommandLine{Context: context}
|
||||
debug := cmd.GlobalBool("debug")
|
||||
debug := cmd.Bool("debug")
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
|
||||
configOptions := strings.Split(cmd.GlobalString("configOverrides"), " ")
|
||||
configOptions := strings.Split(cmd.String("configOverrides"), " ")
|
||||
if err := cfg.Load(&setting.CommandLineArgs{
|
||||
Config: cmd.ConfigFile(),
|
||||
HomePath: cmd.HomePath(),
|
||||
Args: append(configOptions, cmd.Args()...), // tailing arguments have precedence over the options string
|
||||
Args: append(configOptions, cmd.Args().Slice()...), // tailing arguments have precedence over the options string
|
||||
}); err != nil {
|
||||
logger.Errorf("\n%s: Failed to load configuration", color.RedString("Error"))
|
||||
os.Exit(1)
|
||||
return errutil.Wrap("failed to load configuration", err)
|
||||
}
|
||||
|
||||
if debug {
|
||||
@@ -39,80 +38,75 @@ func runDbCommand(command func(commandLine utils.CommandLine, sqlStore *sqlstore
|
||||
engine.Cfg = cfg
|
||||
engine.Bus = bus.GetBus()
|
||||
if err := engine.Init(); err != nil {
|
||||
logger.Errorf("\n%s: Failed to initialize SQL engine", color.RedString("Error"))
|
||||
os.Exit(1)
|
||||
return errutil.Wrap("failed to initialize SQL engine", err)
|
||||
}
|
||||
|
||||
if err := command(cmd, engine); err != nil {
|
||||
logger.Errorf("\n%s: ", color.RedString("Error"))
|
||||
logger.Errorf("%s\n\n", err)
|
||||
|
||||
if err := cmd.ShowHelp(); err != nil {
|
||||
logger.Errorf("\n%s: Failed to show help: %s %s\n\n", color.RedString("Error"),
|
||||
color.RedString("✗"), err)
|
||||
}
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("\n\n")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func runPluginCommand(command func(commandLine utils.CommandLine) error) func(context *cli.Context) {
|
||||
return func(context *cli.Context) {
|
||||
|
||||
func runPluginCommand(command func(commandLine utils.CommandLine) error) func(context *cli.Context) error {
|
||||
return func(context *cli.Context) error {
|
||||
cmd := &utils.ContextCommandLine{Context: context}
|
||||
if err := command(cmd); err != nil {
|
||||
logger.Errorf("\n%s: ", color.RedString("Error"))
|
||||
logger.Errorf("%s %s\n\n", color.RedString("✗"), err)
|
||||
|
||||
if err := cmd.ShowHelp(); err != nil {
|
||||
logger.Errorf("\n%s: Failed to show help: %s %s\n\n", color.RedString("Error"),
|
||||
color.RedString("✗"), err)
|
||||
}
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("\nRestart grafana after installing plugins . <service grafana-server restart>\n\n")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var pluginCommands = []cli.Command{
|
||||
// Command contains command state.
|
||||
type Command struct {
|
||||
Client utils.ApiClient
|
||||
}
|
||||
|
||||
var cmd Command = Command{
|
||||
Client: &services.GrafanaComClient{},
|
||||
}
|
||||
|
||||
var pluginCommands = []*cli.Command{
|
||||
{
|
||||
Name: "install",
|
||||
Usage: "install <plugin id> <plugin version (optional)>",
|
||||
Action: runPluginCommand(installCommand),
|
||||
Action: runPluginCommand(cmd.installCommand),
|
||||
}, {
|
||||
Name: "list-remote",
|
||||
Usage: "list remote available plugins",
|
||||
Action: runPluginCommand(listRemoteCommand),
|
||||
Action: runPluginCommand(cmd.listRemoteCommand),
|
||||
}, {
|
||||
Name: "list-versions",
|
||||
Usage: "list-versions <plugin id>",
|
||||
Action: runPluginCommand(listversionsCommand),
|
||||
Action: runPluginCommand(cmd.listVersionsCommand),
|
||||
}, {
|
||||
Name: "update",
|
||||
Usage: "update <plugin id>",
|
||||
Aliases: []string{"upgrade"},
|
||||
Action: runPluginCommand(upgradeCommand),
|
||||
Action: runPluginCommand(cmd.upgradeCommand),
|
||||
}, {
|
||||
Name: "update-all",
|
||||
Aliases: []string{"upgrade-all"},
|
||||
Usage: "update all your installed plugins",
|
||||
Action: runPluginCommand(upgradeAllCommand),
|
||||
Action: runPluginCommand(cmd.upgradeAllCommand),
|
||||
}, {
|
||||
Name: "ls",
|
||||
Usage: "list all installed plugins",
|
||||
Action: runPluginCommand(lsCommand),
|
||||
Action: runPluginCommand(cmd.lsCommand),
|
||||
}, {
|
||||
Name: "uninstall",
|
||||
Aliases: []string{"remove"},
|
||||
Usage: "uninstall <plugin id>",
|
||||
Action: runPluginCommand(removeCommand),
|
||||
Action: runPluginCommand(cmd.removeCommand),
|
||||
},
|
||||
}
|
||||
|
||||
var adminCommands = []cli.Command{
|
||||
var adminCommands = []*cli.Command{
|
||||
{
|
||||
Name: "reset-admin-password",
|
||||
Usage: "reset-admin-password <new password>",
|
||||
@@ -121,7 +115,7 @@ var adminCommands = []cli.Command{
|
||||
{
|
||||
Name: "data-migration",
|
||||
Usage: "Runs a script that migrates or cleanups data in your db",
|
||||
Subcommands: []cli.Command{
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "encrypt-datasource-passwords",
|
||||
Usage: "Migrates passwords from unsecured fields to secure_json_data field. Return ok unless there is an error. Safe to execute multiple times.",
|
||||
@@ -131,7 +125,7 @@ var adminCommands = []cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var Commands = []cli.Command{
|
||||
var Commands = []*cli.Command{
|
||||
{
|
||||
Name: "plugins",
|
||||
Usage: "Manage plugins for grafana",
|
||||
|
||||
25
pkg/cmd/grafana-cli/commands/commandstest/context.go
Normal file
25
pkg/cmd/grafana-cli/commands/commandstest/context.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package commandstest
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// NewCliContext creates a new CLI context with a certain set of flags.
|
||||
func NewCliContext(flags map[string]string) (*utils.ContextCommandLine, error) {
|
||||
app := cli.App{
|
||||
Name: "Test",
|
||||
}
|
||||
flagSet := flag.NewFlagSet("Test", 0)
|
||||
for flag, value := range flags {
|
||||
flagSet.String(flag, "", "")
|
||||
if err := flagSet.Set(flag, value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &utils.ContextCommandLine{
|
||||
Context: cli.NewContext(&app, flagSet, nil),
|
||||
}, nil
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
package commandstest
|
||||
|
||||
import (
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
|
||||
)
|
||||
|
||||
type FakeFlagger struct {
|
||||
Data map[string]interface{}
|
||||
}
|
||||
|
||||
type FakeCommandLine struct {
|
||||
LocalFlags, GlobalFlags *FakeFlagger
|
||||
HelpShown, VersionShown bool
|
||||
CliArgs []string
|
||||
Client utils.ApiClient
|
||||
}
|
||||
|
||||
func (ff FakeFlagger) String(key string) string {
|
||||
if value, ok := ff.Data[key]; ok {
|
||||
return value.(string)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (ff FakeFlagger) StringSlice(key string) []string {
|
||||
if value, ok := ff.Data[key]; ok {
|
||||
return value.([]string)
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (ff FakeFlagger) Int(key string) int {
|
||||
if value, ok := ff.Data[key]; ok {
|
||||
return value.(int)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (ff FakeFlagger) Bool(key string) bool {
|
||||
if value, ok := ff.Data[key]; ok {
|
||||
return value.(bool)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) String(key string) string {
|
||||
return fcli.LocalFlags.String(key)
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) StringSlice(key string) []string {
|
||||
return fcli.LocalFlags.StringSlice(key)
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) Int(key string) int {
|
||||
return fcli.LocalFlags.Int(key)
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) Bool(key string) bool {
|
||||
if fcli.LocalFlags == nil {
|
||||
return false
|
||||
}
|
||||
return fcli.LocalFlags.Bool(key)
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) GlobalString(key string) string {
|
||||
return fcli.GlobalFlags.String(key)
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) Generic(name string) interface{} {
|
||||
return fcli.LocalFlags.Data[name]
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) FlagNames() []string {
|
||||
flagNames := []string{}
|
||||
for key := range fcli.LocalFlags.Data {
|
||||
flagNames = append(flagNames, key)
|
||||
}
|
||||
|
||||
return flagNames
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) ShowHelp() error {
|
||||
fcli.HelpShown = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) Application() *cli.App {
|
||||
return cli.NewApp()
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) Args() cli.Args {
|
||||
return fcli.CliArgs
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) ShowVersion() {
|
||||
fcli.VersionShown = true
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) RepoDirectory() string {
|
||||
return fcli.GlobalString("repo")
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) PluginDirectory() string {
|
||||
return fcli.GlobalString("pluginsDir")
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) PluginURL() string {
|
||||
return fcli.GlobalString("pluginUrl")
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) ApiClient() utils.ApiClient {
|
||||
return fcli.Client
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPasswordMigrationCommand(t *testing.T) {
|
||||
@@ -45,7 +46,9 @@ func TestPasswordMigrationCommand(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
|
||||
//run migration
|
||||
err = EncryptDatasourcePaswords(&commandstest.FakeCommandLine{}, sqlstore)
|
||||
c, err := commandstest.NewCliContext(map[string]string{})
|
||||
require.Nil(t, err)
|
||||
err = EncryptDatasourcePaswords(c, sqlstore)
|
||||
assert.Nil(t, err)
|
||||
|
||||
//verify that no datasources still have password or basic_auth
|
||||
|
||||
@@ -50,7 +50,7 @@ func validateInput(c utils.CommandLine, pluginFolder string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func installCommand(c utils.CommandLine) error {
|
||||
func (cmd Command) installCommand(c utils.CommandLine) error {
|
||||
pluginFolder := c.PluginDirectory()
|
||||
if err := validateInput(c, pluginFolder); err != nil {
|
||||
return err
|
||||
@@ -59,12 +59,12 @@ func installCommand(c utils.CommandLine) error {
|
||||
pluginToInstall := c.Args().First()
|
||||
version := c.Args().Get(1)
|
||||
|
||||
return InstallPlugin(pluginToInstall, version, c)
|
||||
return InstallPlugin(pluginToInstall, version, c, cmd.Client)
|
||||
}
|
||||
|
||||
// InstallPlugin downloads the plugin code as a zip file from the Grafana.com API
|
||||
// and then extracts the zip into the plugins directory.
|
||||
func InstallPlugin(pluginName, version string, c utils.CommandLine) error {
|
||||
func InstallPlugin(pluginName, version string, c utils.CommandLine, client utils.ApiClient) error {
|
||||
pluginFolder := c.PluginDirectory()
|
||||
downloadURL := c.PluginURL()
|
||||
isInternal := false
|
||||
@@ -78,7 +78,7 @@ func InstallPlugin(pluginName, version string, c utils.CommandLine) error {
|
||||
// is up to the user to know what she is doing.
|
||||
isInternal = true
|
||||
}
|
||||
plugin, err := c.ApiClient().GetPlugin(pluginName, c.RepoDirectory())
|
||||
plugin, err := client.GetPlugin(pluginName, c.RepoDirectory())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -92,7 +92,7 @@ func InstallPlugin(pluginName, version string, c utils.CommandLine) error {
|
||||
version = v.Version
|
||||
}
|
||||
downloadURL = fmt.Sprintf("%s/%s/versions/%s/download",
|
||||
c.GlobalString("repo"),
|
||||
c.String("repo"),
|
||||
pluginName,
|
||||
version,
|
||||
)
|
||||
@@ -111,31 +111,31 @@ func InstallPlugin(pluginName, version string, c utils.CommandLine) error {
|
||||
// Create temp file for downloading zip file
|
||||
tmpFile, err := ioutil.TempFile("", "*.zip")
|
||||
if err != nil {
|
||||
return errutil.Wrap("Failed to create temporary file", err)
|
||||
return errutil.Wrap("failed to create temporary file", err)
|
||||
}
|
||||
defer os.Remove(tmpFile.Name())
|
||||
|
||||
err = c.ApiClient().DownloadFile(pluginName, tmpFile, downloadURL, checksum)
|
||||
err = client.DownloadFile(pluginName, tmpFile, downloadURL, checksum)
|
||||
if err != nil {
|
||||
tmpFile.Close()
|
||||
return errutil.Wrap("Failed to download plugin archive", err)
|
||||
return errutil.Wrap("failed to download plugin archive", err)
|
||||
}
|
||||
err = tmpFile.Close()
|
||||
if err != nil {
|
||||
return errutil.Wrap("Failed to close tmp file", err)
|
||||
return errutil.Wrap("failed to close tmp file", err)
|
||||
}
|
||||
|
||||
err = extractFiles(tmpFile.Name(), pluginName, pluginFolder, isInternal)
|
||||
if err != nil {
|
||||
return errutil.Wrap("Failed to extract plugin archive", err)
|
||||
return errutil.Wrap("failed to extract plugin archive", err)
|
||||
}
|
||||
|
||||
logger.Infof("%s Installed %s successfully \n", color.GreenString("✔"), pluginName)
|
||||
|
||||
res, _ := s.ReadPlugin(pluginFolder, pluginName)
|
||||
for _, v := range res.Dependencies.Plugins {
|
||||
if err := InstallPlugin(v.Id, "", c); err != nil {
|
||||
return errutil.Wrapf(err, "Failed to install plugin '%s'", v.Id)
|
||||
if err := InstallPlugin(v.Id, "", c, client); err != nil {
|
||||
return errutil.Wrapf(err, "failed to install plugin '%s'", v.Id)
|
||||
}
|
||||
|
||||
logger.Infof("Installed dependency: %v ✔\n", v.Id)
|
||||
@@ -179,7 +179,7 @@ func SelectVersion(plugin *m.Plugin, version string) (*m.Version, error) {
|
||||
|
||||
latestForArch := latestSupportedVersion(plugin)
|
||||
if latestForArch == nil {
|
||||
return nil, xerrors.New("Plugin is not supported on your architecture and os.")
|
||||
return nil, xerrors.New("plugin is not supported on your architecture and OS.")
|
||||
}
|
||||
|
||||
if version == "" {
|
||||
@@ -193,11 +193,11 @@ func SelectVersion(plugin *m.Plugin, version string) (*m.Version, error) {
|
||||
}
|
||||
|
||||
if len(ver.Version) == 0 {
|
||||
return nil, xerrors.New("Could not find the version you're looking for")
|
||||
return nil, xerrors.New("could not find the version you're looking for")
|
||||
}
|
||||
|
||||
if !supportsCurrentArch(&ver) {
|
||||
return nil, xerrors.Errorf("Version you want is not supported on your architecture and os. Latest suitable version is %v", latestForArch.Version)
|
||||
return nil, xerrors.Errorf("the version you want is not supported on your architecture and OS. Latest suitable version is %s", latestForArch.Version)
|
||||
}
|
||||
|
||||
return &ver, nil
|
||||
@@ -220,7 +220,7 @@ func extractFiles(archiveFile string, pluginName string, filePath string, allowS
|
||||
for _, zf := range r.File {
|
||||
newFileName := RemoveGitBuildFromName(pluginName, zf.Name)
|
||||
if !isPathSafe(newFileName, filepath.Join(filePath, pluginName)) {
|
||||
return xerrors.Errorf("filepath: %v tries to write outside of plugin directory: %v. This can be a security risk.", zf.Name, path.Join(filePath, pluginName))
|
||||
return xerrors.Errorf("filepath: %q tries to write outside of plugin directory: %q. This can be a security risk.", zf.Name, path.Join(filePath, pluginName))
|
||||
}
|
||||
newFile := path.Join(filePath, newFileName)
|
||||
|
||||
@@ -243,7 +243,7 @@ func extractFiles(archiveFile string, pluginName string, filePath string, allowS
|
||||
} else {
|
||||
err = extractFile(zf, newFile)
|
||||
if err != nil {
|
||||
return errutil.Wrap("Failed to extract file", err)
|
||||
return errutil.Wrap("failed to extract file", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,12 +260,12 @@ func extractSymlink(file *zip.File, filePath string) error {
|
||||
// symlink target is the contents of the file
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
return errutil.Wrap("Failed to extract file", err)
|
||||
return errutil.Wrap("failed to extract file", err)
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = io.Copy(buf, src)
|
||||
if err != nil {
|
||||
return errutil.Wrap("Failed to copy symlink contents", err)
|
||||
return errutil.Wrap("failed to copy symlink contents", err)
|
||||
}
|
||||
err = os.Symlink(strings.TrimSpace(buf.String()), filePath)
|
||||
if err != nil {
|
||||
@@ -292,7 +292,7 @@ func extractFile(file *zip.File, filePath string) (err error) {
|
||||
return fmt.Errorf("file %s is in use. Please stop Grafana, install the plugin and restart Grafana", filePath)
|
||||
}
|
||||
|
||||
return errutil.Wrap("Failed to open file", err)
|
||||
return errutil.Wrap("failed to open file", err)
|
||||
}
|
||||
defer func() {
|
||||
err = dst.Close()
|
||||
@@ -300,7 +300,7 @@ func extractFile(file *zip.File, filePath string) (err error) {
|
||||
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
return errutil.Wrap("Failed to extract file", err)
|
||||
return errutil.Wrap("failed to extract file", err)
|
||||
}
|
||||
defer func() {
|
||||
err = src.Close()
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/commands/commandstest"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFolderNameReplacement(t *testing.T) {
|
||||
@@ -105,11 +105,46 @@ func TestExtractFiles(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInstallPluginCommand(t *testing.T) {
|
||||
pluginDir, del := setupFakePluginsDir(t)
|
||||
defer del()
|
||||
cmd := setupPluginInstallCmd(t, pluginDir)
|
||||
err := InstallPlugin("test-plugin-panel", "", cmd)
|
||||
assert.Nil(t, err)
|
||||
pluginsDir, cleanUp := setupFakePluginsDir(t)
|
||||
defer cleanUp()
|
||||
c, err := commandstest.NewCliContext(map[string]string{"pluginsDir": pluginsDir})
|
||||
require.NoError(t, err)
|
||||
|
||||
client := &commandstest.FakeGrafanaComClient{
|
||||
GetPluginFunc: func(pluginId, repoUrl string) (models.Plugin, error) {
|
||||
require.Equal(t, "test-plugin-panel", pluginId)
|
||||
plugin := models.Plugin{
|
||||
Id: "test-plugin-panel",
|
||||
Category: "",
|
||||
Versions: []models.Version{
|
||||
{
|
||||
Commit: "commit",
|
||||
Url: "url",
|
||||
Version: "1.0.0",
|
||||
Arch: map[string]models.ArchMeta{
|
||||
fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH): {
|
||||
Md5: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return plugin, nil
|
||||
},
|
||||
DownloadFileFunc: func(pluginName string, tmpFile *os.File, url string, checksum string) (err error) {
|
||||
require.Equal(t, "test-plugin-panel", pluginName)
|
||||
require.Equal(t, "/test-plugin-panel/versions/1.0.0/download", url)
|
||||
require.Equal(t, "test", checksum)
|
||||
f, err := os.Open("testdata/grafana-simple-json-datasource-ec18fa4da8096a952608a7e4c7782b4260b41bcf.zip")
|
||||
require.NoError(t, err)
|
||||
_, err = io.Copy(tmpFile, f)
|
||||
require.NoError(t, err)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
err = InstallPlugin("test-plugin-panel", "", c, client)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestIsPathSafe(t *testing.T) {
|
||||
@@ -190,58 +225,13 @@ func TestSelectVersion(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func setupPluginInstallCmd(t *testing.T, pluginDir string) utils.CommandLine {
|
||||
cmd := &commandstest.FakeCommandLine{
|
||||
GlobalFlags: &commandstest.FakeFlagger{Data: map[string]interface{}{
|
||||
"pluginsDir": pluginDir,
|
||||
}},
|
||||
}
|
||||
|
||||
client := &commandstest.FakeGrafanaComClient{}
|
||||
|
||||
client.GetPluginFunc = func(pluginId, repoUrl string) (models.Plugin, error) {
|
||||
assert.Equal(t, "test-plugin-panel", pluginId)
|
||||
plugin := models.Plugin{
|
||||
Id: "test-plugin-panel",
|
||||
Category: "",
|
||||
Versions: []models.Version{
|
||||
{
|
||||
Commit: "commit",
|
||||
Url: "url",
|
||||
Version: "1.0.0",
|
||||
Arch: map[string]models.ArchMeta{
|
||||
fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH): {
|
||||
Md5: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return plugin, nil
|
||||
}
|
||||
|
||||
client.DownloadFileFunc = func(pluginName string, tmpFile *os.File, url string, checksum string) (err error) {
|
||||
assert.Equal(t, "test-plugin-panel", pluginName)
|
||||
assert.Equal(t, "/test-plugin-panel/versions/1.0.0/download", url)
|
||||
assert.Equal(t, "test", checksum)
|
||||
f, err := os.Open("testdata/grafana-simple-json-datasource-ec18fa4da8096a952608a7e4c7782b4260b41bcf.zip")
|
||||
assert.Nil(t, err)
|
||||
_, err = io.Copy(tmpFile, f)
|
||||
assert.Nil(t, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd.Client = client
|
||||
return cmd
|
||||
}
|
||||
|
||||
func setupFakePluginsDir(t *testing.T) (string, func()) {
|
||||
dirname := "testdata/fake-plugins-dir"
|
||||
err := os.RemoveAll(dirname)
|
||||
assert.Nil(t, err)
|
||||
require.Nil(t, err)
|
||||
|
||||
err = os.MkdirAll(dirname, 0774)
|
||||
assert.Nil(t, err)
|
||||
require.Nil(t, err)
|
||||
|
||||
return dirname, func() {
|
||||
err = os.RemoveAll(dirname)
|
||||
|
||||
@@ -7,9 +7,8 @@ import (
|
||||
|
||||
// listRemoteCommand prints out all plugins in the remote repo with latest version supported on current platform.
|
||||
// If there are no supported versions for plugin it is skipped.
|
||||
func listRemoteCommand(c utils.CommandLine) error {
|
||||
plugin, err := c.ApiClient().ListAllPlugins(c.RepoDirectory())
|
||||
|
||||
func (cmd Command) listRemoteCommand(c utils.CommandLine) error {
|
||||
plugin, err := cmd.Client.ListAllPlugins(c.RepoDirectory())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -16,14 +16,14 @@ func validateVersionInput(c utils.CommandLine) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func listversionsCommand(c utils.CommandLine) error {
|
||||
func (cmd Command) listVersionsCommand(c utils.CommandLine) error {
|
||||
if err := validateVersionInput(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pluginToList := c.Args().First()
|
||||
|
||||
plugin, err := c.ApiClient().GetPlugin(pluginToList, c.GlobalString("repo"))
|
||||
plugin, err := cmd.Client.GetPlugin(pluginToList, c.String("repo"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
|
||||
@@ -20,9 +19,8 @@ var validateLsCommand = func(pluginDir string) error {
|
||||
|
||||
logger.Debug("plugindir: " + pluginDir + "\n")
|
||||
pluginDirInfo, err := s.IoHelper.Stat(pluginDir)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("error: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if !pluginDirInfo.IsDir() {
|
||||
@@ -32,7 +30,7 @@ var validateLsCommand = func(pluginDir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func lsCommand(c utils.CommandLine) error {
|
||||
func (cmd Command) lsCommand(c utils.CommandLine) error {
|
||||
pluginDir := c.PluginDirectory()
|
||||
if err := validateLsCommand(pluginDir); err != nil {
|
||||
return err
|
||||
@@ -45,7 +43,7 @@ func lsCommand(c utils.CommandLine) error {
|
||||
}
|
||||
|
||||
for _, plugin := range plugins {
|
||||
logger.Infof("%s %s %s \n", plugin.Id, color.YellowString("@"), plugin.Info.Version)
|
||||
logger.Infof("%s %s %s\n", plugin.Id, color.YellowString("@"), plugin.Info.Version)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -14,76 +14,55 @@ func TestMissingPath(t *testing.T) {
|
||||
Convey("ls command", t, func() {
|
||||
validateLsCommand = org
|
||||
|
||||
Convey("Missing path", func() {
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"ls"},
|
||||
GlobalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"path": "",
|
||||
},
|
||||
},
|
||||
}
|
||||
Convey("Missing path flag", func() {
|
||||
cmd := Command{}
|
||||
c, err := commandstest.NewCliContext(map[string]string{})
|
||||
So(err, ShouldBeNil)
|
||||
s.IoHelper = &commandstest.FakeIoUtil{}
|
||||
|
||||
Convey("should return error", func() {
|
||||
err := lsCommand(commandLine)
|
||||
So(err, ShouldNotBeNil)
|
||||
err := cmd.lsCommand(c)
|
||||
So(err, ShouldBeError, "missing path flag")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Path is not a directory", func() {
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"ls"},
|
||||
GlobalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"path": "/var/lib/grafana/plugins",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
c, err := commandstest.NewCliContext(map[string]string{"path": "/var/lib/grafana/plugins"})
|
||||
So(err, ShouldBeNil)
|
||||
s.IoHelper = &commandstest.FakeIoUtil{
|
||||
FakeIsDirectory: false,
|
||||
}
|
||||
cmd := Command{}
|
||||
|
||||
Convey("should return error", func() {
|
||||
err := lsCommand(commandLine)
|
||||
err := cmd.lsCommand(c)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("can override validateLsCommand", func() {
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"ls"},
|
||||
GlobalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"path": "/var/lib/grafana/plugins",
|
||||
},
|
||||
},
|
||||
}
|
||||
c, err := commandstest.NewCliContext(map[string]string{"path": "/var/lib/grafana/plugins"})
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
validateLsCommand = func(pluginDir string) error {
|
||||
return errors.New("dummie error")
|
||||
return errors.New("dummy error")
|
||||
}
|
||||
|
||||
Convey("should return error", func() {
|
||||
err := lsCommand(commandLine)
|
||||
So(err.Error(), ShouldEqual, "dummie error")
|
||||
cmd := Command{}
|
||||
err := cmd.lsCommand(c)
|
||||
So(err.Error(), ShouldEqual, "dummy error")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Validate that validateLsCommand is reset", func() {
|
||||
commandLine := &commandstest.FakeCommandLine{
|
||||
CliArgs: []string{"ls"},
|
||||
GlobalFlags: &commandstest.FakeFlagger{
|
||||
Data: map[string]interface{}{
|
||||
"path": "/var/lib/grafana/plugins",
|
||||
},
|
||||
},
|
||||
}
|
||||
c, err := commandstest.NewCliContext(map[string]string{"path": "/var/lib/grafana/plugins"})
|
||||
So(err, ShouldBeNil)
|
||||
cmd := Command{}
|
||||
|
||||
Convey("should return error", func() {
|
||||
err := lsCommand(commandLine)
|
||||
So(err.Error(), ShouldNotEqual, "dummie error")
|
||||
err := cmd.lsCommand(c)
|
||||
So(err.Error(), ShouldNotEqual, "dummy error")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -11,19 +11,19 @@ import (
|
||||
|
||||
var removePlugin func(pluginPath, id string) error = services.RemoveInstalledPlugin
|
||||
|
||||
func removeCommand(c utils.CommandLine) error {
|
||||
func (cmd Command) removeCommand(c utils.CommandLine) error {
|
||||
pluginPath := c.PluginDirectory()
|
||||
|
||||
plugin := c.Args().First()
|
||||
if plugin == "" {
|
||||
return errors.New("Missing plugin parameter")
|
||||
return errors.New("missing plugin parameter")
|
||||
}
|
||||
|
||||
err := removePlugin(pluginPath, plugin)
|
||||
|
||||
if err != nil {
|
||||
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
|
||||
|
||||
@@ -20,13 +20,13 @@ func resetPasswordCommand(c utils.CommandLine, sqlStore *sqlstore.SqlStore) erro
|
||||
|
||||
password := models.Password(newPassword)
|
||||
if password.IsWeak() {
|
||||
return fmt.Errorf("New password is too short")
|
||||
return fmt.Errorf("new password is too short")
|
||||
}
|
||||
|
||||
userQuery := models.GetUserByIdQuery{Id: AdminUserId}
|
||||
|
||||
if err := bus.Dispatch(&userQuery); err != nil {
|
||||
return fmt.Errorf("Could not read user from database. Error: %v", err)
|
||||
return fmt.Errorf("could not read user from database. Error: %v", err)
|
||||
}
|
||||
|
||||
passwordHashed, err := util.EncodePassword(newPassword, userQuery.Result.Salt)
|
||||
@@ -40,7 +40,7 @@ func resetPasswordCommand(c utils.CommandLine, sqlStore *sqlstore.SqlStore) erro
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
return errutil.Wrapf(err, "Failed to update user password")
|
||||
return errutil.Wrapf(err, "failed to update user password")
|
||||
}
|
||||
|
||||
logger.Infof("\n")
|
||||
|
||||
@@ -22,13 +22,12 @@ func shouldUpgrade(installed string, remote *m.Plugin) bool {
|
||||
return installedVersion.LessThan(latestVersion)
|
||||
}
|
||||
|
||||
func upgradeAllCommand(c utils.CommandLine) error {
|
||||
func (cmd Command) upgradeAllCommand(c utils.CommandLine) error {
|
||||
pluginsDir := c.PluginDirectory()
|
||||
|
||||
localPlugins := s.GetLocalPlugins(pluginsDir)
|
||||
|
||||
remotePlugins, err := c.ApiClient().ListAllPlugins(c.GlobalString("repo"))
|
||||
|
||||
remotePlugins, err := cmd.Client.ListAllPlugins(c.String("repo"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -53,7 +52,7 @@ func upgradeAllCommand(c utils.CommandLine) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = InstallPlugin(p.Id, "", c)
|
||||
err = InstallPlugin(p.Id, "", c, cmd.Client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
)
|
||||
|
||||
func upgradeCommand(c utils.CommandLine) error {
|
||||
func (cmd Command) upgradeCommand(c utils.CommandLine) error {
|
||||
pluginsDir := c.PluginDirectory()
|
||||
pluginName := c.Args().First()
|
||||
|
||||
@@ -18,18 +18,17 @@ func upgradeCommand(c utils.CommandLine) error {
|
||||
return err
|
||||
}
|
||||
|
||||
plugin, err2 := c.ApiClient().GetPlugin(pluginName, c.RepoDirectory())
|
||||
|
||||
plugin, err2 := cmd.Client.GetPlugin(pluginName, c.RepoDirectory())
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
|
||||
if shouldUpgrade(localPlugin.Info.Version, &plugin) {
|
||||
if err := s.RemoveInstalledPlugin(pluginsDir, pluginName); err != nil {
|
||||
return errutil.Wrapf(err, "Failed to remove plugin '%s'", pluginName)
|
||||
return errutil.Wrapf(err, "failed to remove plugin '%s'", pluginName)
|
||||
}
|
||||
|
||||
return InstallPlugin(pluginName, "", c)
|
||||
return InstallPlugin(pluginName, "", c, cmd.Client)
|
||||
}
|
||||
|
||||
logger.Infof("%s %s is up to date \n", color.GreenString("✔"), pluginName)
|
||||
|
||||
@@ -5,11 +5,12 @@ import (
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/fatih/color"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/commands"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var version = "master"
|
||||
@@ -17,63 +18,67 @@ var version = "master"
|
||||
func main() {
|
||||
setupLogging()
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Name = "Grafana cli"
|
||||
app.Usage = ""
|
||||
app.Author = "Grafana Project"
|
||||
app.Email = "https://github.com/grafana/grafana"
|
||||
app.Version = version
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "pluginsDir",
|
||||
Usage: "path to the grafana plugin directory",
|
||||
Value: utils.GetGrafanaPluginDir(runtime.GOOS),
|
||||
EnvVar: "GF_PLUGIN_DIR",
|
||||
app := &cli.App{
|
||||
Name: "Grafana CLI",
|
||||
Authors: []*cli.Author{
|
||||
{
|
||||
Name: "Grafana Project",
|
||||
Email: "hello@grafana.com",
|
||||
},
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "repo",
|
||||
Usage: "url to the plugin repository",
|
||||
Value: "https://grafana.com/api/plugins",
|
||||
EnvVar: "GF_PLUGIN_REPO",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "pluginUrl",
|
||||
Usage: "Full url to the plugin zip file instead of downloading the plugin from grafana.com/api",
|
||||
Value: "",
|
||||
EnvVar: "GF_PLUGIN_URL",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "insecure",
|
||||
Usage: "Skip TLS verification (insecure)",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "debug, d",
|
||||
Usage: "enable debug logging",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "configOverrides",
|
||||
Usage: "configuration options to override defaults as a string. e.g. cfg:default.paths.log=/dev/null",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "homepath",
|
||||
Usage: "path to grafana install/home path, defaults to working directory",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "config",
|
||||
Usage: "path to config file",
|
||||
Version: version,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "pluginsDir",
|
||||
Usage: "Path to the Grafana plugin directory",
|
||||
Value: utils.GetGrafanaPluginDir(runtime.GOOS),
|
||||
EnvVars: []string{"GF_PLUGIN_DIR"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "repo",
|
||||
Usage: "URL to the plugin repository",
|
||||
Value: "https://grafana.com/api/plugins",
|
||||
EnvVars: []string{"GF_PLUGIN_REPO"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "pluginUrl",
|
||||
Usage: "Full url to the plugin zip file instead of downloading the plugin from grafana.com/api",
|
||||
Value: "",
|
||||
EnvVars: []string{"GF_PLUGIN_URL"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "insecure",
|
||||
Usage: "Skip TLS verification (insecure)",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "debug, d",
|
||||
Usage: "Enable debug logging",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "configOverrides",
|
||||
Usage: "Configuration options to override defaults as a string. e.g. cfg:default.paths.log=/dev/null",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "homepath",
|
||||
Usage: "Path to Grafana install/home path, defaults to working directory",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "config",
|
||||
Usage: "Path to config file",
|
||||
},
|
||||
},
|
||||
Commands: commands.Commands,
|
||||
CommandNotFound: cmdNotFound,
|
||||
}
|
||||
|
||||
app.Before = func(c *cli.Context) error {
|
||||
services.Init(version, c.GlobalBool("insecure"))
|
||||
services.Init(version, c.Bool("insecure"))
|
||||
return nil
|
||||
}
|
||||
app.Commands = commands.Commands
|
||||
app.CommandNotFound = cmdNotFound
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
logger.Errorf("%v", err)
|
||||
logger.Errorf("%s: %s %s\n", color.RedString("Error"), color.RedString("✗"), err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,8 @@ package utils
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
type CommandLine interface {
|
||||
@@ -17,14 +16,12 @@ type CommandLine interface {
|
||||
Int(name string) int
|
||||
String(name string) string
|
||||
StringSlice(name string) []string
|
||||
GlobalString(name string) string
|
||||
FlagNames() (names []string)
|
||||
Generic(name string) interface{}
|
||||
|
||||
PluginDirectory() string
|
||||
RepoDirectory() string
|
||||
PluginURL() string
|
||||
ApiClient() ApiClient
|
||||
}
|
||||
|
||||
type ApiClient interface {
|
||||
@@ -49,26 +46,22 @@ func (c *ContextCommandLine) Application() *cli.App {
|
||||
return c.App
|
||||
}
|
||||
|
||||
func (c *ContextCommandLine) HomePath() string { return c.GlobalString("homepath") }
|
||||
func (c *ContextCommandLine) HomePath() string { return c.String("homepath") }
|
||||
|
||||
func (c *ContextCommandLine) ConfigFile() string { return c.GlobalString("config") }
|
||||
func (c *ContextCommandLine) ConfigFile() string { return c.String("config") }
|
||||
|
||||
func (c *ContextCommandLine) PluginDirectory() string {
|
||||
return c.GlobalString("pluginsDir")
|
||||
return c.String("pluginsDir")
|
||||
}
|
||||
|
||||
func (c *ContextCommandLine) RepoDirectory() string {
|
||||
return c.GlobalString("repo")
|
||||
return c.String("repo")
|
||||
}
|
||||
|
||||
func (c *ContextCommandLine) PluginURL() string {
|
||||
return c.GlobalString("pluginUrl")
|
||||
return c.String("pluginUrl")
|
||||
}
|
||||
|
||||
func (c *ContextCommandLine) OptionsString() string {
|
||||
return c.GlobalString("configOverrides")
|
||||
}
|
||||
|
||||
func (c *ContextCommandLine) ApiClient() ApiClient {
|
||||
return &services.GrafanaComClient{}
|
||||
return c.String("configOverrides")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user