Plugins: Fail plugins installation on wrong args provided (#71355)

* Return error on plugin install extra args

* Remove unused valid argument

* Add log for wrong installation args
This commit is contained in:
Hugo Kiyodi Oshiro 2023-07-12 13:52:12 +02:00 committed by GitHub
parent 82a5770376
commit fe4a932c6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 357 additions and 3 deletions

View File

@ -19,7 +19,14 @@ import (
"github.com/grafana/grafana/pkg/plugins/storage"
)
func validateInput(c utils.CommandLine, pluginFolder string) error {
const installArgsSize = 2
func validateInput(c utils.CommandLine) error {
if c.Args().Len() > installArgsSize {
logger.Info(color.RedString("Please specify the correct format. For example ./grafana cli (<command arguments>) plugins install <plugin ID> (<plugin version>)\n\n"))
return errors.New("install only supports 2 arguments: plugin and version")
}
arg := c.Args().First()
if arg == "" {
return errors.New("please specify plugin to install")
@ -50,8 +57,7 @@ func logRestartNotice() {
}
func installCommand(c utils.CommandLine) error {
pluginFolder := c.PluginDirectory()
if err := validateInput(c, pluginFolder); err != nil {
if err := validateInput(c); err != nil {
return err
}

View File

@ -0,0 +1,26 @@
package commands
import (
"testing"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
"github.com/stretchr/testify/require"
)
func TestValidateInput(t *testing.T) {
t.Run("should print message for ignored args", func(t *testing.T) {
mockCmdLine := &utils.MockCommandLine{}
defer mockCmdLine.AssertExpectations(t)
cmdArgs := []string{"foo", "bar", "--bar=foo"}
mockArgs := &utils.MockArgs{}
defer mockArgs.AssertExpectations(t)
mockArgs.On("Len").Return(len(cmdArgs))
mockCmdLine.On("Args").Return(mockArgs).Times(1)
err := validateInput(mockCmdLine)
require.EqualError(t, err, "install only supports 2 arguments: plugin and version")
})
}

View File

@ -0,0 +1,112 @@
// Code generated by mockery v2.31.4. DO NOT EDIT.
package utils
import mock "github.com/stretchr/testify/mock"
// MockArgs is an autogenerated mock type for the Args type
type MockArgs struct {
mock.Mock
}
// First provides a mock function with given fields:
func (_m *MockArgs) First() string {
ret := _m.Called()
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// Get provides a mock function with given fields: n
func (_m *MockArgs) Get(n int) string {
ret := _m.Called(n)
var r0 string
if rf, ok := ret.Get(0).(func(int) string); ok {
r0 = rf(n)
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// Len provides a mock function with given fields:
func (_m *MockArgs) Len() int {
ret := _m.Called()
var r0 int
if rf, ok := ret.Get(0).(func() int); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(int)
}
return r0
}
// Present provides a mock function with given fields:
func (_m *MockArgs) Present() bool {
ret := _m.Called()
var r0 bool
if rf, ok := ret.Get(0).(func() bool); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// Slice provides a mock function with given fields:
func (_m *MockArgs) Slice() []string {
ret := _m.Called()
var r0 []string
if rf, ok := ret.Get(0).(func() []string); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]string)
}
}
return r0
}
// Tail provides a mock function with given fields:
func (_m *MockArgs) Tail() []string {
ret := _m.Called()
var r0 []string
if rf, ok := ret.Get(0).(func() []string); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]string)
}
}
return r0
}
// NewMockArgs creates a new instance of MockArgs. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockArgs(t interface {
mock.TestingT
Cleanup(func())
}) *MockArgs {
mock := &MockArgs{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -0,0 +1,210 @@
// Code generated by mockery v2.31.4. DO NOT EDIT.
package utils
import (
mock "github.com/stretchr/testify/mock"
cli "github.com/urfave/cli/v2"
)
// MockCommandLine is an autogenerated mock type for the CommandLine type
type MockCommandLine struct {
mock.Mock
}
// Application provides a mock function with given fields:
func (_m *MockCommandLine) Application() *cli.App {
ret := _m.Called()
var r0 *cli.App
if rf, ok := ret.Get(0).(func() *cli.App); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*cli.App)
}
}
return r0
}
// Args provides a mock function with given fields:
func (_m *MockCommandLine) Args() cli.Args {
ret := _m.Called()
var r0 cli.Args
if rf, ok := ret.Get(0).(func() cli.Args); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(cli.Args)
}
}
return r0
}
// Bool provides a mock function with given fields: name
func (_m *MockCommandLine) Bool(name string) bool {
ret := _m.Called(name)
var r0 bool
if rf, ok := ret.Get(0).(func(string) bool); ok {
r0 = rf(name)
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// FlagNames provides a mock function with given fields:
func (_m *MockCommandLine) FlagNames() []string {
ret := _m.Called()
var r0 []string
if rf, ok := ret.Get(0).(func() []string); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]string)
}
}
return r0
}
// Generic provides a mock function with given fields: name
func (_m *MockCommandLine) Generic(name string) interface{} {
ret := _m.Called(name)
var r0 interface{}
if rf, ok := ret.Get(0).(func(string) interface{}); ok {
r0 = rf(name)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(interface{})
}
}
return r0
}
// Int provides a mock function with given fields: name
func (_m *MockCommandLine) Int(name string) int {
ret := _m.Called(name)
var r0 int
if rf, ok := ret.Get(0).(func(string) int); ok {
r0 = rf(name)
} else {
r0 = ret.Get(0).(int)
}
return r0
}
// PluginDirectory provides a mock function with given fields:
func (_m *MockCommandLine) PluginDirectory() string {
ret := _m.Called()
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// PluginRepoURL provides a mock function with given fields:
func (_m *MockCommandLine) PluginRepoURL() string {
ret := _m.Called()
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// PluginURL provides a mock function with given fields:
func (_m *MockCommandLine) PluginURL() string {
ret := _m.Called()
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// ShowHelp provides a mock function with given fields:
func (_m *MockCommandLine) ShowHelp() error {
ret := _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// ShowVersion provides a mock function with given fields:
func (_m *MockCommandLine) ShowVersion() {
_m.Called()
}
// String provides a mock function with given fields: name
func (_m *MockCommandLine) String(name string) string {
ret := _m.Called(name)
var r0 string
if rf, ok := ret.Get(0).(func(string) string); ok {
r0 = rf(name)
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// StringSlice provides a mock function with given fields: name
func (_m *MockCommandLine) StringSlice(name string) []string {
ret := _m.Called(name)
var r0 []string
if rf, ok := ret.Get(0).(func(string) []string); ok {
r0 = rf(name)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]string)
}
}
return r0
}
// NewMockCommandLine creates a new instance of MockCommandLine. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockCommandLine(t interface {
mock.TestingT
Cleanup(func())
}) *MockCommandLine {
mock := &MockCommandLine{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}