grafana-cli: Upgrade to urfave/cli v2 (#22402)

* grafana-cli: Upgrade to urfave/cli v2
This commit is contained in:
Arve Knudsen
2020-02-26 12:27:31 +01:00
committed by GitHub
parent 6bc369629d
commit eb98d9c15b
84 changed files with 10503 additions and 4755 deletions

View File

@@ -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",

View 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
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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()

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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")
})
})
})

View File

@@ -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

View File

@@ -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")

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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)
}
}

View File

@@ -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")
}