install_command_test.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. package commands
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "runtime"
  7. "testing"
  8. "github.com/grafana/grafana/pkg/cmd/grafana-cli/commands/commandstest"
  9. "github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
  10. "github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
  11. . "github.com/smartystreets/goconvey/convey"
  12. "github.com/stretchr/testify/assert"
  13. )
  14. func TestFoldernameReplacement(t *testing.T) {
  15. Convey("path containing git commit path", t, func() {
  16. pluginName := "datasource-plugin-kairosdb"
  17. paths := map[string]string{
  18. "datasource-plugin-kairosdb-cc4a3965ef5d3eb1ae0ee4f93e9e78ec7db69e64/": "datasource-plugin-kairosdb/",
  19. "datasource-plugin-kairosdb-cc4a3965ef5d3eb1ae0ee4f93e9e78ec7db69e64/README.md": "datasource-plugin-kairosdb/README.md",
  20. "datasource-plugin-kairosdb-cc4a3965ef5d3eb1ae0ee4f93e9e78ec7db69e64/partials/": "datasource-plugin-kairosdb/partials/",
  21. "datasource-plugin-kairosdb-cc4a3965ef5d3eb1ae0ee4f93e9e78ec7db69e64/partials/config.html": "datasource-plugin-kairosdb/partials/config.html",
  22. }
  23. Convey("should be replaced with plugin name", func() {
  24. for k, v := range paths {
  25. So(RemoveGitBuildFromName(pluginName, k), ShouldEqual, v)
  26. }
  27. })
  28. })
  29. Convey("path containing git commit path", t, func() {
  30. pluginName := "app-example"
  31. paths := map[string]string{
  32. "app-plugin-example-3c28f65ac6fb7f1e234b0364b97081d836495439/": "app-example/",
  33. }
  34. Convey("should be replaced with plugin name", func() {
  35. for k, v := range paths {
  36. So(RemoveGitBuildFromName(pluginName, k), ShouldEqual, v)
  37. }
  38. })
  39. })
  40. }
  41. func TestExtractFiles(t *testing.T) {
  42. t.Run("Should preserve file permissions for plugin backend binaries for linux and darwin", func(t *testing.T) {
  43. skipWindows(t)
  44. pluginDir, del := setupFakePluginsDir(t)
  45. defer del()
  46. body, err := ioutil.ReadFile("testdata/grafana-simple-json-datasource-ec18fa4da8096a952608a7e4c7782b4260b41bcf.zip")
  47. assert.Nil(t, err)
  48. err = extractFiles(body, "grafana-simple-json-datasource", pluginDir, false)
  49. assert.Nil(t, err)
  50. //File in zip has permissions 755
  51. fileInfo, err := os.Stat(pluginDir + "/grafana-simple-json-datasource/simple-plugin_darwin_amd64")
  52. assert.Nil(t, err)
  53. assert.Equal(t, "-rwxr-xr-x", fileInfo.Mode().String())
  54. //File in zip has permission 755
  55. fileInfo, err = os.Stat(pluginDir + "/grafana-simple-json-datasource/simple-plugin_linux_amd64")
  56. assert.Nil(t, err)
  57. assert.Equal(t, "-rwxr-xr-x", fileInfo.Mode().String())
  58. //File in zip has permission 644
  59. fileInfo, err = os.Stat(pluginDir + "/grafana-simple-json-datasource/simple-plugin_windows_amd64.exe")
  60. assert.Nil(t, err)
  61. assert.Equal(t, "-rw-r--r--", fileInfo.Mode().String())
  62. //File in zip has permission 755
  63. fileInfo, err = os.Stat(pluginDir + "/grafana-simple-json-datasource/non-plugin-binary")
  64. assert.Nil(t, err)
  65. assert.Equal(t, "-rwxr-xr-x", fileInfo.Mode().String())
  66. })
  67. t.Run("Should ignore symlinks if not allowed", func(t *testing.T) {
  68. pluginDir, del := setupFakePluginsDir(t)
  69. defer del()
  70. body, err := ioutil.ReadFile("testdata/plugin-with-symlink.zip")
  71. assert.Nil(t, err)
  72. err = extractFiles(body, "plugin-with-symlink", pluginDir, false)
  73. assert.Nil(t, err)
  74. _, err = os.Stat(pluginDir + "/plugin-with-symlink/text.txt")
  75. assert.Nil(t, err)
  76. _, err = os.Stat(pluginDir + "/plugin-with-symlink/symlink_to_txt")
  77. assert.NotNil(t, err)
  78. })
  79. t.Run("Should extract symlinks if allowed", func(t *testing.T) {
  80. skipWindows(t)
  81. pluginDir, del := setupFakePluginsDir(t)
  82. defer del()
  83. body, err := ioutil.ReadFile("testdata/plugin-with-symlink.zip")
  84. assert.Nil(t, err)
  85. err = extractFiles(body, "plugin-with-symlink", pluginDir, true)
  86. assert.Nil(t, err)
  87. _, err = os.Stat(pluginDir + "/plugin-with-symlink/symlink_to_txt")
  88. assert.Nil(t, err)
  89. fmt.Println(err)
  90. })
  91. }
  92. func TestInstallPluginCommand(t *testing.T) {
  93. pluginDir, del := setupFakePluginsDir(t)
  94. defer del()
  95. cmd := setupPluginInstallCmd(t, pluginDir)
  96. err := InstallPlugin("test-plugin-panel", "", cmd)
  97. assert.Nil(t, err)
  98. }
  99. func TestIsPathSafe(t *testing.T) {
  100. dest := fmt.Sprintf("%stest%spath", string(os.PathSeparator), string(os.PathSeparator))
  101. t.Run("Should be true on nested destinations", func(t *testing.T) {
  102. assert.True(t, isPathSafe("dest", dest))
  103. assert.True(t, isPathSafe("dest/one", dest))
  104. assert.True(t, isPathSafe("../path/dest/one", dest))
  105. })
  106. t.Run("Should be false on destinations outside of path", func(t *testing.T) {
  107. assert.False(t, isPathSafe("../dest", dest))
  108. assert.False(t, isPathSafe("../../", dest))
  109. assert.False(t, isPathSafe("../../test", dest))
  110. })
  111. }
  112. func setupPluginInstallCmd(t *testing.T, pluginDir string) utils.CommandLine {
  113. cmd := &commandstest.FakeCommandLine{
  114. GlobalFlags: &commandstest.FakeFlagger{Data: map[string]interface{}{
  115. "pluginsDir": pluginDir,
  116. }},
  117. }
  118. client := &commandstest.FakeGrafanaComClient{}
  119. client.GetPluginFunc = func(pluginId, repoUrl string) (models.Plugin, error) {
  120. assert.Equal(t, "test-plugin-panel", pluginId)
  121. plugin := models.Plugin{
  122. Id: "test-plugin-panel",
  123. Category: "",
  124. Versions: []models.Version{
  125. {
  126. Commit: "commit",
  127. Url: "url",
  128. Version: "1.0.0",
  129. Arch: map[string]models.ArchMeta{
  130. fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH): {
  131. Md5: "test",
  132. },
  133. },
  134. },
  135. },
  136. }
  137. return plugin, nil
  138. }
  139. client.DownloadFileFunc = func(pluginName, filePath, url string, checksum string) (content []byte, err error) {
  140. assert.Equal(t, "test-plugin-panel", pluginName)
  141. assert.Equal(t, "/test-plugin-panel/versions/1.0.0/download", url)
  142. assert.Equal(t, "test", checksum)
  143. body, err := ioutil.ReadFile("testdata/grafana-simple-json-datasource-ec18fa4da8096a952608a7e4c7782b4260b41bcf.zip")
  144. assert.Nil(t, err)
  145. return body, nil
  146. }
  147. cmd.Client = client
  148. return cmd
  149. }
  150. func setupFakePluginsDir(t *testing.T) (string, func()) {
  151. dirname := "testdata/fake-plugins-dir"
  152. err := os.RemoveAll(dirname)
  153. assert.Nil(t, err)
  154. err = os.MkdirAll(dirname, 0774)
  155. assert.Nil(t, err)
  156. return dirname, func() {
  157. err = os.RemoveAll(dirname)
  158. assert.Nil(t, err)
  159. }
  160. }
  161. func skipWindows(t *testing.T) {
  162. if runtime.GOOS == "windows" {
  163. t.Skip("Skipping test on Windows")
  164. }
  165. }