diff --git a/pkg/cmd/grafana-cli/commands/install_command.go b/pkg/cmd/grafana-cli/commands/install_command.go index e09d84a9721..bba6f52db98 100644 --- a/pkg/cmd/grafana-cli/commands/install_command.go +++ b/pkg/cmd/grafana-cli/commands/install_command.go @@ -90,6 +90,7 @@ type pluginInstallOpts struct { repoURL string pluginURL string pluginDir string + gcomToken string } func newInstallPluginOpts(c utils.CommandLine) pluginInstallOpts { @@ -98,6 +99,7 @@ func newInstallPluginOpts(c utils.CommandLine) pluginInstallOpts { repoURL: c.PluginRepoURL(), pluginURL: c.PluginURL(), pluginDir: c.PluginDirectory(), + gcomToken: c.GcomToken(), } } @@ -132,9 +134,10 @@ func doInstallPlugin(ctx context.Context, pluginID, version string, o pluginInst } repository := repo.NewManager(repo.ManagerCfg{ - SkipTLSVerify: o.insecure, - BaseURL: o.repoURL, - Logger: services.Logger, + SkipTLSVerify: o.insecure, + BaseURL: o.repoURL, + Logger: services.Logger, + GrafanaComAPIToken: o.gcomToken, }) // FIXME: Re-enable grafanaVersion. This check was broken in 10.2 so disabling it for the moment. diff --git a/pkg/cmd/grafana-cli/commands/install_command_test.go b/pkg/cmd/grafana-cli/commands/install_command_test.go index bfb286f58d5..9fcf045d70b 100644 --- a/pkg/cmd/grafana-cli/commands/install_command_test.go +++ b/pkg/cmd/grafana-cli/commands/install_command_test.go @@ -106,7 +106,8 @@ func TestValidatePluginRepoConfig(t *testing.T) { t.Skip("skipping integration test") } grafDir, cfgPath := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{ - GrafanaComAPIURL: "https://grafana-dev.com", + GrafanaComAPIURL: "https://grafana-dev.com", + GrafanaComSSOAPIToken: "token3", }) c, err := commandstest.NewCliContext(map[string]string{ @@ -116,6 +117,9 @@ func TestValidatePluginRepoConfig(t *testing.T) { require.NoError(t, err) repoURL := c.PluginRepoURL() require.Equal(t, "https://grafana-dev.com/plugins", repoURL) + + token := c.GcomToken() + require.Equal(t, "token3", token) }) t.Run("Should use config overrides parameter if it is set alongside config parameter", func(t *testing.T) { diff --git a/pkg/cmd/grafana-cli/utils/command_line.go b/pkg/cmd/grafana-cli/utils/command_line.go index e394ab03769..6ce00ad60a9 100644 --- a/pkg/cmd/grafana-cli/utils/command_line.go +++ b/pkg/cmd/grafana-cli/utils/command_line.go @@ -26,6 +26,7 @@ type CommandLine interface { PluginDirectory() string PluginRepoURL() string PluginURL() string + GcomToken() string } type ApiClient interface { @@ -75,12 +76,7 @@ func (c *ContextCommandLine) PluginRepoURL() string { // if --config flag is set, try to get the GrafanaComAPIURL setting if c.ConfigFile() != "" { - configOptions := strings.Split(c.String("configOverrides"), " ") - cfg, err := setting.NewCfgFromArgs(setting.CommandLineArgs{ - Config: c.ConfigFile(), - HomePath: c.HomePath(), - Args: append(configOptions, c.Args().Slice()...), - }) + cfg, err := c.Config() if err != nil { logger.Debug("Could not parse config file", err) @@ -92,6 +88,25 @@ func (c *ContextCommandLine) PluginRepoURL() string { return c.String("repo") } +func (c *ContextCommandLine) Config() (*setting.Cfg, error) { + configOptions := strings.Split(c.String("configOverrides"), " ") + return setting.NewCfgFromArgs(setting.CommandLineArgs{ + Config: c.ConfigFile(), + HomePath: c.HomePath(), + Args: append(configOptions, c.Args().Slice()...), + }) +} + +func (c *ContextCommandLine) GcomToken() string { + cfg, err := c.Config() + + if err != nil { + logger.Debug("Could not parse config file", err) + return "" + } + return cfg.GrafanaComSSOAPIToken +} + func (c *ContextCommandLine) PluginURL() string { return c.String("pluginUrl") } diff --git a/pkg/cmd/grafana-cli/utils/command_line_mock.go b/pkg/cmd/grafana-cli/utils/command_line_mock.go index 09cf896bc93..45195a2de0a 100644 --- a/pkg/cmd/grafana-cli/utils/command_line_mock.go +++ b/pkg/cmd/grafana-cli/utils/command_line_mock.go @@ -146,6 +146,19 @@ func (_m *MockCommandLine) PluginURL() string { return r0 } +func (_m *MockCommandLine) GcomToken() 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() diff --git a/pkg/plugins/repo/client.go b/pkg/plugins/repo/client.go index 29293537a66..1c4c6496ece 100644 --- a/pkg/plugins/repo/client.go +++ b/pkg/plugins/repo/client.go @@ -14,6 +14,7 @@ import ( "net/http" "net/url" "os" + "strings" "time" "github.com/grafana/grafana/pkg/plugins/log" @@ -24,16 +25,18 @@ type Client struct { httpClientNoTimeout http.Client retryCount int grafanaComAPIToken string + grafanaComAPIURL string log log.PrettyLogger } -func NewClient(skipTLSVerify bool, grafanaComAPIToken string, logger log.PrettyLogger) *Client { +func NewClient(skipTLSVerify bool, grafanaComAPIToken, grafanaComAPIURL string, logger log.PrettyLogger) *Client { return &Client{ httpClient: MakeHttpClient(skipTLSVerify, 10*time.Second), httpClientNoTimeout: MakeHttpClient(skipTLSVerify, 0), log: logger, grafanaComAPIToken: grafanaComAPIToken, + grafanaComAPIURL: grafanaComAPIURL, } } @@ -233,7 +236,7 @@ func (c *Client) createReq(ctx context.Context, url *url.URL, compatOpts CompatO req.Header.Set("grafana-origin", orig.(string)) } - if c.grafanaComAPIToken != "" { + if strings.HasPrefix(url.String(), c.grafanaComAPIURL) && c.grafanaComAPIToken != "" { req.Header.Set("Authorization", "Bearer "+c.grafanaComAPIToken) } diff --git a/pkg/plugins/repo/client_test.go b/pkg/plugins/repo/client_test.go index 6ffdc76c2a7..ccc526386aa 100644 --- a/pkg/plugins/repo/client_test.go +++ b/pkg/plugins/repo/client_test.go @@ -70,7 +70,7 @@ func Test_Download(t *testing.T) { require.Equal(t, 2, count, "should retry on error") }) - t.Run("it should use gcom token when it's available", func(t *testing.T) { + t.Run("it should use gcom token when the token is available and request is to GCOM", func(t *testing.T) { expectedToken := "token-test" var gcomCalled bool fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -82,9 +82,29 @@ func Test_Download(t *testing.T) { })) defer fakeServer.Close() cli := fakeServer.Client() - repo := Client{httpClient: *cli, httpClientNoTimeout: *cli, log: log.NewPrettyLogger("test"), grafanaComAPIToken: expectedToken} - _, err := repo.Download(context.Background(), fakeServer.URL, "", CompatOpts{}) + pluginURL := fakeServer.URL + "/api/plugins/test-datasource" + gcomAPIURL := fakeServer.URL + "/api/plugins" + repo := Client{httpClient: *cli, httpClientNoTimeout: *cli, log: log.NewPrettyLogger("test"), grafanaComAPIToken: expectedToken, grafanaComAPIURL: gcomAPIURL} + _, err := repo.Download(context.Background(), pluginURL, "", CompatOpts{}) require.NoError(t, err) require.True(t, gcomCalled) }) + + t.Run("it should not set gcom token when the token is available but request is NOT to GCOM", func(t *testing.T) { + expectedToken := "token-test" + var serverCalled bool + fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + token := r.Header.Get("Authorization") + require.Empty(t, token, "token should not be set") + err := writeFakeZip(w) + require.NoError(t, err) + serverCalled = true + })) + defer fakeServer.Close() + cli := fakeServer.Client() + repo := Client{httpClient: *cli, httpClientNoTimeout: *cli, log: log.NewPrettyLogger("test"), grafanaComAPIToken: expectedToken, grafanaComAPIURL: "https://grafana.com/api/plugins"} + _, err := repo.Download(context.Background(), fakeServer.URL, "", CompatOpts{}) + require.NoError(t, err) + require.True(t, serverCalled) + }) } diff --git a/pkg/plugins/repo/service.go b/pkg/plugins/repo/service.go index 8007a03d609..a7a1a7f8660 100644 --- a/pkg/plugins/repo/service.go +++ b/pkg/plugins/repo/service.go @@ -14,8 +14,7 @@ import ( ) type Manager struct { - client *Client - baseURL string + client *Client log log.PrettyLogger } @@ -43,9 +42,8 @@ type ManagerCfg struct { func NewManager(cfg ManagerCfg) *Manager { return &Manager{ - baseURL: cfg.BaseURL, - client: NewClient(cfg.SkipTLSVerify, cfg.GrafanaComAPIToken, cfg.Logger), - log: cfg.Logger, + client: NewClient(cfg.SkipTLSVerify, cfg.GrafanaComAPIToken, cfg.BaseURL, cfg.Logger), + log: cfg.Logger, } } @@ -101,12 +99,12 @@ func (m *Manager) PluginVersion(ctx context.Context, pluginID, version string, c } func (m *Manager) downloadURL(pluginID, version string) string { - return fmt.Sprintf("%s/%s/versions/%s/download", m.baseURL, pluginID, version) + return fmt.Sprintf("%s/%s/versions/%s/download", m.client.grafanaComAPIURL, pluginID, version) } // grafanaCompatiblePluginVersions will get version info from /api/plugins/$pluginID/versions func (m *Manager) grafanaCompatiblePluginVersions(ctx context.Context, pluginID string, compatOpts CompatOpts) ([]Version, error) { - u, err := url.Parse(m.baseURL) + u, err := url.Parse(m.client.grafanaComAPIURL) if err != nil { return nil, err } diff --git a/pkg/tests/testinfra/testinfra.go b/pkg/tests/testinfra/testinfra.go index 84ebf182703..6010c712356 100644 --- a/pkg/tests/testinfra/testinfra.go +++ b/pkg/tests/testinfra/testinfra.go @@ -149,6 +149,8 @@ func StartGrafanaEnv(t *testing.T, grafDir, cfgPath string) (string, *server.Tes // CreateGrafDir creates the Grafana directory. // The log by default is muted in the regression test, to activate it, pass option EnableLog = true +// +//nolint:gocyclo func CreateGrafDir(t *testing.T, opts GrafanaOpts) (string, string) { t.Helper() @@ -459,6 +461,13 @@ func CreateGrafDir(t *testing.T, opts GrafanaOpts) (string, string) { _, err = grafanaComSection.NewKey("api_url", opts.GrafanaComAPIURL) require.NoError(t, err) } + if opts.GrafanaComSSOAPIToken != "" { + grafanaComSection, err := getOrCreateSection("grafana_com") + require.NoError(t, err) + _, err = grafanaComSection.NewKey("sso_api_token", opts.GrafanaComSSOAPIToken) + require.NoError(t, err) + } + if opts.UnifiedStorageConfig != nil { for k, v := range opts.UnifiedStorageConfig { section, err := getOrCreateSection(fmt.Sprintf("unified_storage.%s", k)) @@ -519,6 +528,7 @@ type GrafanaOpts struct { QueryRetries int64 GrafanaComAPIURL string UnifiedStorageConfig map[string]setting.UnifiedStorageConfig + GrafanaComSSOAPIToken string // When "unified-grpc" is selected it will also start the grpc server APIServerStorageType options.StorageType