2016-02-15 21:09:34 +08:00
package commands
import (
2021-05-13 02:05:16 +08:00
"context"
2016-02-15 21:09:34 +08:00
"errors"
2016-03-10 21:43:21 +08:00
"fmt"
2016-02-15 21:09:34 +08:00
"os"
2019-07-29 16:44:58 +08:00
"runtime"
2016-03-13 18:29:43 +08:00
"strings"
2016-03-29 03:42:26 +08:00
2023-02-22 16:24:13 +08:00
"github.com/fatih/color"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
2021-04-26 22:13:40 +08:00
"github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
2019-05-27 16:47:21 +08:00
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
2022-08-23 17:50:50 +08:00
"github.com/grafana/grafana/pkg/plugins/repo"
"github.com/grafana/grafana/pkg/plugins/storage"
2016-02-15 21:09:34 +08:00
)
2019-05-27 16:47:21 +08:00
func validateInput ( c utils . CommandLine , pluginFolder string ) error {
2016-02-15 21:09:34 +08:00
arg := c . Args ( ) . First ( )
if arg == "" {
return errors . New ( "please specify plugin to install" )
}
2016-06-25 02:14:58 +08:00
pluginsDir := c . PluginDirectory ( )
2016-03-29 03:42:26 +08:00
if pluginsDir == "" {
return errors . New ( "missing pluginsDir flag" )
2016-02-15 21:09:34 +08:00
}
2016-03-29 03:42:26 +08:00
fileInfo , err := os . Stat ( pluginsDir )
2016-03-11 21:11:25 +08:00
if err != nil {
2016-03-29 03:42:26 +08:00
if err = os . MkdirAll ( pluginsDir , os . ModePerm ) ; err != nil {
2018-04-17 02:25:48 +08:00
return fmt . Errorf ( "pluginsDir (%s) is not a writable directory" , pluginsDir )
2016-03-11 21:11:25 +08:00
}
return nil
}
if ! fileInfo . IsDir ( ) {
2016-02-15 21:09:34 +08:00
return errors . New ( "path is not a directory" )
}
return nil
}
2023-02-22 16:24:13 +08:00
func logRestartNotice ( ) {
logger . Info ( color . GreenString ( "Please restart Grafana after installing or removing plugins. Refer to Grafana documentation for instructions if necessary.\n\n" ) )
}
2020-02-26 19:27:31 +08:00
func ( cmd Command ) installCommand ( c utils . CommandLine ) error {
2016-06-25 02:14:58 +08:00
pluginFolder := c . PluginDirectory ( )
2016-02-15 21:09:34 +08:00
if err := validateInput ( c , pluginFolder ) ; err != nil {
return err
}
2021-04-26 22:13:40 +08:00
pluginID := c . Args ( ) . First ( )
2016-02-15 21:09:34 +08:00
version := c . Args ( ) . Get ( 1 )
2023-02-22 16:24:13 +08:00
err := installPlugin ( context . Background ( ) , pluginID , version , c )
if err == nil {
logRestartNotice ( )
}
return err
2016-02-15 21:09:34 +08:00
}
2022-08-23 17:50:50 +08:00
// installPlugin downloads the plugin code as a zip file from the Grafana.com API
// and then extracts the zip into the plugin's directory.
func installPlugin ( ctx context . Context , pluginID , version string , c utils . CommandLine ) error {
2022-06-15 20:38:59 +08:00
skipTLSVerify := c . Bool ( "insecure" )
2022-08-23 17:50:50 +08:00
repository := repo . New ( skipTLSVerify , c . PluginRepoURL ( ) , services . Logger )
2018-02-16 16:49:29 +08:00
2022-08-23 17:50:50 +08:00
compatOpts := repo . NewCompatOpts ( services . GrafanaVersion , runtime . GOOS , runtime . GOARCH )
var archive * repo . PluginArchive
var err error
pluginZipURL := c . PluginURL ( )
if pluginZipURL != "" {
if archive , err = repository . GetPluginArchiveByURL ( ctx , pluginZipURL , compatOpts ) ; err != nil {
return err
}
} else {
if archive , err = repository . GetPluginArchive ( ctx , pluginID , version , compatOpts ) ; err != nil {
return err
}
}
pluginFs := storage . FileSystem ( services . Logger , c . PluginDirectory ( ) )
2023-04-20 17:52:59 +08:00
extractedArchive , err := pluginFs . Extract ( ctx , pluginID , archive . File )
2022-08-23 17:50:50 +08:00
if err != nil {
return err
}
for _ , dep := range extractedArchive . Dependencies {
services . Logger . Infof ( "Fetching %s dependency..." , dep . ID )
d , err := repository . GetPluginArchive ( ctx , dep . ID , dep . Version , compatOpts )
if err != nil {
return fmt . Errorf ( "%v: %w" , fmt . Sprintf ( "failed to download plugin %s from repository" , dep . ID ) , err )
}
2023-04-20 17:52:59 +08:00
_ , err = pluginFs . Extract ( ctx , dep . ID , d . File )
2022-08-23 17:50:50 +08:00
if err != nil {
return err
}
}
return nil
2016-02-15 21:09:34 +08:00
}
2019-07-29 16:44:58 +08:00
func osAndArchString ( ) string {
osString := strings . ToLower ( runtime . GOOS )
arch := runtime . GOARCH
return osString + "-" + arch
}
2020-03-04 20:18:13 +08:00
func supportsCurrentArch ( version * models . Version ) bool {
2019-07-29 16:44:58 +08:00
if version . Arch == nil {
return true
}
for arch := range version . Arch {
if arch == osAndArchString ( ) || arch == "any" {
return true
}
}
return false
}
2020-03-04 20:18:13 +08:00
func latestSupportedVersion ( plugin * models . Plugin ) * models . Version {
2020-06-26 14:46:08 +08:00
for _ , v := range plugin . Versions {
ver := v
2019-07-29 16:44:58 +08:00
if supportsCurrentArch ( & ver ) {
return & ver
}
}
return nil
}