Renderer: Fix regression on callback URL in plugin mode (#103787)
* Renderer: Fix regression on callback URL in plugin mode * cleanup * refactor to not mutate cfg object * apply review feedback
This commit is contained in:
@@ -121,7 +121,7 @@ func TestCapabilities(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
rs.Cfg.RendererUrl = tt.rendererUrl
|
||||
rs.Cfg.RendererServerUrl = tt.rendererUrl
|
||||
rs.version = tt.rendererVersion
|
||||
res, err := rs.HasCapability(context.Background(), tt.capabilityName)
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ func (rs *RenderingService) renderCSVViaHTTP(ctx context.Context, renderKey stri
|
||||
}
|
||||
|
||||
func (rs *RenderingService) generateImageRendererURL(renderType RenderType, opts Opts, renderKey string) (*url.URL, error) {
|
||||
rendererUrl := rs.Cfg.RendererUrl
|
||||
rendererUrl := rs.Cfg.RendererServerUrl
|
||||
if renderType == RenderCSV {
|
||||
rendererUrl += "/csv"
|
||||
}
|
||||
@@ -242,7 +242,7 @@ func (rs *RenderingService) getRemotePluginVersionWithRetry(callback func(string
|
||||
}
|
||||
|
||||
func (rs *RenderingService) getRemotePluginVersion() (string, error) {
|
||||
rendererURL, err := url.Parse(rs.Cfg.RendererUrl + "/version")
|
||||
rendererURL, err := url.Parse(rs.Cfg.RendererServerUrl + "/version")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -27,18 +27,19 @@ import (
|
||||
var _ Service = (*RenderingService)(nil)
|
||||
|
||||
type RenderingService struct {
|
||||
log log.Logger
|
||||
plugin Plugin
|
||||
renderAction renderFunc
|
||||
renderCSVAction renderCSVFunc
|
||||
sanitizeSVGAction sanitizeFunc
|
||||
sanitizeURL string
|
||||
domain string
|
||||
inProgressCount int32
|
||||
version string
|
||||
versionMutex sync.RWMutex
|
||||
capabilities []Capability
|
||||
pluginAvailable bool
|
||||
log log.Logger
|
||||
plugin Plugin
|
||||
renderAction renderFunc
|
||||
renderCSVAction renderCSVFunc
|
||||
sanitizeSVGAction sanitizeFunc
|
||||
sanitizeURL string
|
||||
domain string
|
||||
inProgressCount int32
|
||||
version string
|
||||
versionMutex sync.RWMutex
|
||||
capabilities []Capability
|
||||
pluginAvailable bool
|
||||
rendererCallbackURL string
|
||||
|
||||
perRequestRenderKeyProvider renderKeyProvider
|
||||
Cfg *setting.Cfg
|
||||
@@ -80,9 +81,24 @@ func ProvideService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, remot
|
||||
// value used for domain attribute of renderKey cookie
|
||||
var domain string
|
||||
|
||||
// value used by the image renderer to make requests to Grafana
|
||||
rendererCallbackURL := cfg.RendererCallbackUrl
|
||||
if cfg.RendererServerUrl != "" {
|
||||
sanitizeURL = getSanitizerURL(cfg.RendererServerUrl)
|
||||
|
||||
// Default value for callback URL using a remote renderer should be AppURL
|
||||
if rendererCallbackURL == "" {
|
||||
rendererCallbackURL = cfg.AppURL
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case cfg.RendererCallbackUrl != "":
|
||||
u, err := url.Parse(cfg.RendererCallbackUrl)
|
||||
case rendererCallbackURL != "":
|
||||
if rendererCallbackURL[len(rendererCallbackURL)-1] != '/' {
|
||||
rendererCallbackURL += "/"
|
||||
}
|
||||
|
||||
u, err := url.Parse(rendererCallbackURL)
|
||||
if err != nil {
|
||||
logger.Warn("Image renderer callback url is not valid. " +
|
||||
"Please provide a valid RendererCallbackUrl. " +
|
||||
@@ -96,10 +112,6 @@ func ProvideService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, remot
|
||||
domain = "localhost"
|
||||
}
|
||||
|
||||
if cfg.RendererUrl != "" {
|
||||
sanitizeURL = getSanitizerURL(cfg.RendererUrl)
|
||||
}
|
||||
|
||||
var renderKeyProvider renderKeyProvider
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagRenderAuthJWT) {
|
||||
renderKeyProvider = &jwtRenderKeyProvider{
|
||||
@@ -145,6 +157,7 @@ func ProvideService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, remot
|
||||
domain: domain,
|
||||
sanitizeURL: sanitizeURL,
|
||||
pluginAvailable: exists,
|
||||
rendererCallbackURL: rendererCallbackURL,
|
||||
}
|
||||
|
||||
gob.Register(&RenderUser{})
|
||||
@@ -215,7 +228,7 @@ func (rs *RenderingService) Run(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (rs *RenderingService) remoteAvailable() bool {
|
||||
return rs.Cfg.RendererUrl != ""
|
||||
return rs.Cfg.RendererServerUrl != ""
|
||||
}
|
||||
|
||||
func (rs *RenderingService) IsAvailable(ctx context.Context) bool {
|
||||
@@ -424,13 +437,14 @@ func (rs *RenderingService) getNewFilePath(rt RenderType) (string, error) {
|
||||
|
||||
// getGrafanaCallbackURL creates a URL to send to the image rendering as callback for rendering a Grafana resource
|
||||
func (rs *RenderingService) getGrafanaCallbackURL(path string) string {
|
||||
if rs.Cfg.RendererUrl != "" || rs.Cfg.RendererCallbackUrl != "" {
|
||||
// The backend rendering service can potentially be remote.
|
||||
// So we need to use the root_url to ensure the rendering service
|
||||
// can reach this Grafana instance.
|
||||
if rs.rendererCallbackURL != "" {
|
||||
// rendererCallbackURL should be set if:
|
||||
// - the backend rendering service is remote (default value is cfg.AppURL
|
||||
// and set when initializing the service)
|
||||
// - the service is a plugin and Grafana is running behind a proxy changing its domain
|
||||
|
||||
// &render=1 signals to the legacy redirect layer to
|
||||
return fmt.Sprintf("%s%s&render=1", rs.Cfg.RendererCallbackUrl, path)
|
||||
return fmt.Sprintf("%s%s&render=1", rs.rendererCallbackURL, path)
|
||||
}
|
||||
|
||||
protocol := rs.Cfg.Protocol
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
@@ -25,21 +26,21 @@ func TestGetUrl(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("When renderer and callback url configured should return callback url plus path", func(t *testing.T) {
|
||||
rs.Cfg.RendererUrl = "http://localhost:8081/render"
|
||||
rs.Cfg.RendererCallbackUrl = "http://public-grafana.com/"
|
||||
rs.Cfg.RendererServerUrl = "http://localhost:8081/render"
|
||||
rs.rendererCallbackURL = "http://public-grafana.com/"
|
||||
url := rs.getGrafanaCallbackURL(path)
|
||||
require.Equal(t, rs.Cfg.RendererCallbackUrl+path+"&render=1", url)
|
||||
require.Equal(t, rs.rendererCallbackURL+path+"&render=1", url)
|
||||
})
|
||||
|
||||
t.Run("When callback url is configured and https should return domain of callback url plus path", func(t *testing.T) {
|
||||
rs.Cfg.RendererCallbackUrl = "https://public-grafana.com/"
|
||||
rs.rendererCallbackURL = "https://public-grafana.com/"
|
||||
url := rs.getGrafanaCallbackURL(path)
|
||||
require.Equal(t, rs.Cfg.RendererCallbackUrl+path+"&render=1", url)
|
||||
require.Equal(t, rs.rendererCallbackURL+path+"&render=1", url)
|
||||
})
|
||||
|
||||
t.Run("When renderer url not configured", func(t *testing.T) {
|
||||
rs.Cfg.RendererUrl = ""
|
||||
rs.Cfg.RendererCallbackUrl = ""
|
||||
rs.Cfg.RendererServerUrl = ""
|
||||
rs.rendererCallbackURL = ""
|
||||
rs.domain = "localhost"
|
||||
rs.Cfg.HTTPPort = "3000"
|
||||
|
||||
@@ -128,8 +129,8 @@ func TestRenderLimitImage(t *testing.T) {
|
||||
|
||||
rs := RenderingService{
|
||||
Cfg: &setting.Cfg{
|
||||
HomePath: path,
|
||||
RendererUrl: "http://localhost:8081/render",
|
||||
HomePath: path,
|
||||
RendererServerUrl: "http://localhost:8081/render",
|
||||
},
|
||||
inProgressCount: 2,
|
||||
log: log.New("test"),
|
||||
@@ -170,7 +171,7 @@ func TestRenderLimitImage(t *testing.T) {
|
||||
func TestRenderLimitImageError(t *testing.T) {
|
||||
rs := RenderingService{
|
||||
Cfg: &setting.Cfg{
|
||||
RendererUrl: "http://localhost:8081/render",
|
||||
RendererServerUrl: "http://localhost:8081/render",
|
||||
},
|
||||
inProgressCount: 2,
|
||||
log: log.New("test"),
|
||||
@@ -201,7 +202,7 @@ func TestRenderingServiceGetRemotePluginVersion(t *testing.T) {
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
rs.Cfg.RendererUrl = server.URL + "/render"
|
||||
rs.Cfg.RendererServerUrl = server.URL + "/render"
|
||||
version, err := rs.getRemotePluginVersion()
|
||||
|
||||
require.NoError(t, err)
|
||||
@@ -214,7 +215,7 @@ func TestRenderingServiceGetRemotePluginVersion(t *testing.T) {
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
rs.Cfg.RendererUrl = server.URL + "/render"
|
||||
rs.Cfg.RendererServerUrl = server.URL + "/render"
|
||||
version, err := rs.getRemotePluginVersion()
|
||||
|
||||
require.NoError(t, err)
|
||||
@@ -239,7 +240,7 @@ func TestRenderingServiceGetRemotePluginVersion(t *testing.T) {
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
rs.Cfg.RendererUrl = server.URL + "/render"
|
||||
rs.Cfg.RendererServerUrl = server.URL + "/render"
|
||||
remoteVersionFetchInterval = time.Millisecond
|
||||
remoteVersionFetchRetries = 5
|
||||
go func() {
|
||||
@@ -249,3 +250,76 @@ func TestRenderingServiceGetRemotePluginVersion(t *testing.T) {
|
||||
require.Eventually(t, func() bool { return rs.Version() == "3.1.4159" }, time.Second, time.Millisecond)
|
||||
})
|
||||
}
|
||||
|
||||
func TestProvideService(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.AppURL = "http://app-url"
|
||||
cfg.ImagesDir = filepath.Join(t.TempDir(), "images")
|
||||
cfg.CSVsDir = filepath.Join(t.TempDir(), "csvs")
|
||||
cfg.PDFsDir = filepath.Join(t.TempDir(), "pdfs")
|
||||
|
||||
t.Run("Default configuration values", func(t *testing.T) {
|
||||
rs, err := ProvideService(cfg, featuremgmt.WithFeatures(), nil, &dummyPluginManager{})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, "", rs.Cfg.RendererServerUrl)
|
||||
require.Equal(t, "", rs.rendererCallbackURL)
|
||||
require.Equal(t, "", rs.domain)
|
||||
})
|
||||
|
||||
t.Run("RendererURL is set but not RendererCallbackUrl", func(t *testing.T) {
|
||||
cfg.RendererServerUrl = "http://custom-renderer:8081"
|
||||
cfg.RendererCallbackUrl = ""
|
||||
|
||||
rs, err := ProvideService(cfg, featuremgmt.WithFeatures(), nil, &dummyPluginManager{})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, "http://custom-renderer:8081", rs.Cfg.RendererServerUrl)
|
||||
require.Equal(t, "http://app-url/", rs.rendererCallbackURL)
|
||||
require.Equal(t, "app-url", rs.domain)
|
||||
})
|
||||
|
||||
t.Run("RendererURL and RendererCallbackUrl are set", func(t *testing.T) {
|
||||
cfg.RendererServerUrl = "http://custom-renderer:8081"
|
||||
cfg.RendererCallbackUrl = "http://public-grafana.com/"
|
||||
|
||||
rs, err := ProvideService(cfg, featuremgmt.WithFeatures(), nil, &dummyPluginManager{})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, "http://custom-renderer:8081", rs.Cfg.RendererServerUrl)
|
||||
require.Equal(t, "http://public-grafana.com/", rs.rendererCallbackURL)
|
||||
require.Equal(t, "public-grafana.com", rs.domain)
|
||||
})
|
||||
|
||||
t.Run("RendererURL is not set but RendererCallbackUrl is set", func(t *testing.T) {
|
||||
cfg.RendererServerUrl = ""
|
||||
cfg.RendererCallbackUrl = "https://public-grafana.com/"
|
||||
|
||||
rs, err := ProvideService(cfg, featuremgmt.WithFeatures(), nil, &dummyPluginManager{})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, "", rs.Cfg.RendererServerUrl)
|
||||
require.Equal(t, "https://public-grafana.com/", rs.rendererCallbackURL)
|
||||
require.Equal(t, "public-grafana.com", rs.domain)
|
||||
})
|
||||
|
||||
t.Run("RendererCallbackURL is missing trailing slash", func(t *testing.T) {
|
||||
cfg.RendererServerUrl = ""
|
||||
cfg.RendererCallbackUrl = "https://public-grafana.com"
|
||||
|
||||
rs, err := ProvideService(cfg, featuremgmt.WithFeatures(), nil, &dummyPluginManager{})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, "", rs.Cfg.RendererServerUrl)
|
||||
require.Equal(t, "https://public-grafana.com/", rs.rendererCallbackURL)
|
||||
require.Equal(t, "public-grafana.com", rs.domain)
|
||||
})
|
||||
|
||||
t.Run("RendererCallbackURL is invalid", func(t *testing.T) {
|
||||
cfg.RendererServerUrl = ""
|
||||
cfg.RendererCallbackUrl = "http://public{grafana"
|
||||
|
||||
_, err := ProvideService(cfg, featuremgmt.WithFeatures(), nil, &dummyPluginManager{})
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user